Commit e6959ba8 authored by Jérome Perrin's avatar Jérome Perrin

oauth2_authorisation: py3

parent 4f423de4
...@@ -70,7 +70,7 @@ from DateTime import DateTime ...@@ -70,7 +70,7 @@ from DateTime import DateTime
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.Message import translateString from Products.ERP5Type.Message import translateString
from Products.ERP5Type.UnrestrictedMethod import super_user from Products.ERP5Type.UnrestrictedMethod import super_user
from Products.ERP5Type.Utils import bytes2str, str2bytes, unicode2str from Products.ERP5Type.Utils import bytes2str, str2bytes, unicode2str, str2unicode
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Security.ERP5GroupManager import ( from Products.ERP5Security.ERP5GroupManager import (
disableCache as ERP5GroupManager_disableCache, disableCache as ERP5GroupManager_disableCache,
...@@ -196,6 +196,7 @@ def substituteRequest( ...@@ -196,6 +196,7 @@ def substituteRequest(
request_container.REQUEST = inner_request request_container.REQUEST = inner_request
try: try:
__traceback_info__ = inner_request __traceback_info__ = inner_request
print('inner_request =>', inner_request.text())
yield inner_request yield inner_request
finally: finally:
request_container.REQUEST = request_from_container request_container.REQUEST = request_from_container
...@@ -448,7 +449,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint): ...@@ -448,7 +449,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
} }
for x in ( for x in (
portal.portal_categories.resolveCategory( portal.portal_categories.resolveCategory(
'oauth2_scope/' + y.encode('utf-8'), 'oauth2_scope/' + unicode2str(y),
) )
for y in scope_list for y in scope_list
) )
...@@ -699,7 +700,7 @@ class _ERP5RequestValidator(RequestValidator): ...@@ -699,7 +700,7 @@ class _ERP5RequestValidator(RequestValidator):
client_value=request.client.erp5_client_value, client_value=request.client.erp5_client_value,
redirect_uri=request.redirect_uri, redirect_uri=request.redirect_uri,
scope_list=[ scope_list=[
x.encode('utf-8') unicode2str(x)
for x in request.scopes for x in request.scopes
], ],
code_challenge=request.code_challenge, code_challenge=request.code_challenge,
...@@ -859,13 +860,13 @@ def _callEndpoint(endpoint, self, REQUEST): ...@@ -859,13 +860,13 @@ def _callEndpoint(endpoint, self, REQUEST):
# not have to care about intermediate proxies). # not have to care about intermediate proxies).
request_header_dict['X_FORWARDED_FOR'] = REQUEST.getClientAddr() request_header_dict['X_FORWARDED_FOR'] = REQUEST.getClientAddr()
request_body = REQUEST.get('BODY') request_body = REQUEST.get('BODY')
if request_body is None and content_type == 'application/x-www-form-urlencoded': if (not request_body) and content_type == 'application/x-www-form-urlencoded':
# XXX: very imperfect, but should be good enough for OAuth2 usage: # XXX: very imperfect, but should be good enough for OAuth2 usage:
# no standard OAuth2 POST field should be marshalled by Zope. # no standard OAuth2 POST field should be marshalled by Zope.
request_body = urlencode([ request_body = urlencode([
(x, y) (x, y)
for x, y in six.iteritems(REQUEST.form) for x, y in six.iteritems(REQUEST.form)
if isinstance(y, six.text_type) if isinstance(y, six.string_types)
]) ])
uri = other.get('URL', '') uri = other.get('URL', '')
query_string = environ.get('QUERY_STRING') query_string = environ.get('QUERY_STRING')
...@@ -1316,7 +1317,7 @@ class OAuth2AuthorisationServerConnector(XMLObject): ...@@ -1316,7 +1317,7 @@ class OAuth2AuthorisationServerConnector(XMLObject):
Validate non-standard jwt claims against request. Validate non-standard jwt claims against request.
""" """
if not isAddressInNetworkList( if not isAddressInNetworkList(
address=request.headers['X_FORWARDED_FOR'].decode('utf-8'), address=str2unicode(request.headers['X_FORWARDED_FOR']),
network_list=token[JWT_CLAIM_NETWORK_LIST_KEY], network_list=token[JWT_CLAIM_NETWORK_LIST_KEY],
): ):
raise jwt.InvalidTokenError raise jwt.InvalidTokenError
...@@ -1399,9 +1400,9 @@ class OAuth2AuthorisationServerConnector(XMLObject): ...@@ -1399,9 +1400,9 @@ class OAuth2AuthorisationServerConnector(XMLObject):
def _getSessionValueFromTokenDict(self, token_dict): def _getSessionValueFromTokenDict(self, token_dict):
session_value = self._getSessionValue( session_value = self._getSessionValue(
token_dict[JWT_PAYLOAD_KEY][ unicode2str(token_dict[JWT_PAYLOAD_KEY][
JWT_PAYLOAD_AUTHORISATION_SESSION_ID_KEY JWT_PAYLOAD_AUTHORISATION_SESSION_ID_KEY
].encode('utf-8'), ]),
'validated', 'validated',
) )
if session_value is not None: if session_value is not None:
......
...@@ -5,17 +5,20 @@ Mutate REQUEST to call standard OAuth2 /authorize endpoint from an ERP5 Form in ...@@ -5,17 +5,20 @@ Mutate REQUEST to call standard OAuth2 /authorize endpoint from an ERP5 Form in
import json import json
import six import six
from erp5.component.document.OAuth2AuthorisationServerConnector import substituteRequest from erp5.component.document.OAuth2AuthorisationServerConnector import substituteRequest
from Products.ERP5Type.Utils import unicode2str
# XXX: Accessing REQUEST from acquisition is bad. But Base_callDialogMethod # XXX: Accessing REQUEST from acquisition is bad. But Base_callDialogMethod
# does not propagate the request cleanly, so no other way so far. # does not propagate the request cleanly, so no other way so far.
REQUEST = context.REQUEST REQUEST = context.REQUEST
form = { form = {
key.encode('utf-8'): value.encode('utf-8') unicode2str(key): unicode2str(value)
for key, value in six.iteritems(json.loads(request_info_json)) for key, value in six.iteritems(json.loads(request_info_json))
} }
if scope_list: if scope_list:
form['scopes'] = ' '.join(scope_list) form['scopes'] = ' '.join(scope_list)
portal = context.getPortalObject() portal = context.getPortalObject()
from pprint import pprint
pprint(('substituteRequest form', substituteRequest))
with substituteRequest( with substituteRequest(
context=portal, context=portal,
request=REQUEST, request=REQUEST,
......
...@@ -44,6 +44,8 @@ form = dict(parse_qsl(parsed_came_from.query)) ...@@ -44,6 +44,8 @@ form = dict(parse_qsl(parsed_came_from.query))
login_retry_url = REQUEST.form.get('login_retry_url') login_retry_url = REQUEST.form.get('login_retry_url')
if login_retry_url is not None: if login_retry_url is not None:
form['login_retry_url'] = login_retry_url form['login_retry_url'] = login_retry_url
from pprint import pprint
pprint(('logged_in_once substituteRequest', form))
with substituteRequest( with substituteRequest(
context=portal, context=portal,
request=REQUEST, request=REQUEST,
......
...@@ -45,7 +45,7 @@ from six.moves.urllib.parse import parse_qsl, quote, unquote, urlencode, urlspli ...@@ -45,7 +45,7 @@ from six.moves.urllib.parse import parse_qsl, quote, unquote, urlencode, urlspli
from AccessControl.SecurityManagement import getSecurityManager, setSecurityManager from AccessControl.SecurityManagement import getSecurityManager, setSecurityManager
from DateTime import DateTime from DateTime import DateTime
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.Utils import bytes2str, str2bytes from Products.ERP5Type.Utils import bytes2str, str2bytes, unicode2str
from Products.ERP5.ERP5Site import ( from Products.ERP5.ERP5Site import (
ERP5_AUTHORISATION_EXTRACTOR_USERNAME_NAME, ERP5_AUTHORISATION_EXTRACTOR_USERNAME_NAME,
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME, ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME,
...@@ -81,7 +81,7 @@ class FormExtractor(HTMLParser): ...@@ -81,7 +81,7 @@ class FormExtractor(HTMLParser):
elif self.__in_form and tag in _HTML_FIELD_TAG_SET: elif self.__in_form and tag in _HTML_FIELD_TAG_SET:
self.form_list[-1][1].append(( self.form_list[-1][1].append((
attr_dict['name'], attr_dict['name'],
attr_dict.get('value', '').encode('utf-8'), unicode2str(attr_dict.get('value', ''))
)) ))
def handle_endtag(self, tag): def handle_endtag(self, tag):
...@@ -853,13 +853,13 @@ class TestOAuth2(ERP5TypeTestCase): ...@@ -853,13 +853,13 @@ class TestOAuth2(ERP5TypeTestCase):
path=oauth2_server_connector + '/token', path=oauth2_server_connector + '/token',
method='POST', method='POST',
content_type='application/x-www-form-urlencoded', content_type='application/x-www-form-urlencoded',
body=urlencode({ body=str2bytes(urlencode({
'grant_type': 'authorization_code', 'grant_type': 'authorization_code',
'code': authorisation_code, 'code': authorisation_code,
'client_id': client_id, 'client_id': client_id,
'code_verifier': code_verifier, 'code_verifier': code_verifier,
'redirect_uri': _EXTERNAL_CLIENT_REDIRECT_URI, 'redirect_uri': _EXTERNAL_CLIENT_REDIRECT_URI,
}), })),
) )
time_after = int(time()) time_after = int(time())
self.assertEqual(status, 200, response) self.assertEqual(status, 200, response)
...@@ -881,10 +881,10 @@ class TestOAuth2(ERP5TypeTestCase): ...@@ -881,10 +881,10 @@ class TestOAuth2(ERP5TypeTestCase):
path=oauth2_server_connector + '/token', path=oauth2_server_connector + '/token',
method='POST', method='POST',
content_type='application/x-www-form-urlencoded', content_type='application/x-www-form-urlencoded',
body=urlencode({ body=str2bytes(urlencode({
'grant_type': 'refresh_token', 'grant_type': 'refresh_token',
'refresh_token': refresh_token, 'refresh_token': refresh_token,
}), })),
) )
self.assertEqual(status, 200) self.assertEqual(status, 200)
self.assertEqual(cookie_dict, {}) self.assertEqual(cookie_dict, {})
...@@ -898,10 +898,10 @@ class TestOAuth2(ERP5TypeTestCase): ...@@ -898,10 +898,10 @@ class TestOAuth2(ERP5TypeTestCase):
path=oauth2_server_connector + '/revoke', path=oauth2_server_connector + '/revoke',
method='POST', method='POST',
content_type='application/x-www-form-urlencoded', content_type='application/x-www-form-urlencoded',
body=urlencode({ body=str2bytes(urlencode({
'token_type_hint': 'refresh_token', 'token_type_hint': 'refresh_token',
'token': refresh_token, 'token': refresh_token,
}), })),
) )
self.assertEqual(status, 200) self.assertEqual(status, 200)
self.assertEqual(cookie_dict, {}) self.assertEqual(cookie_dict, {})
...@@ -914,10 +914,10 @@ class TestOAuth2(ERP5TypeTestCase): ...@@ -914,10 +914,10 @@ class TestOAuth2(ERP5TypeTestCase):
path=oauth2_server_connector + '/token', path=oauth2_server_connector + '/token',
method='POST', method='POST',
content_type='application/x-www-form-urlencoded', content_type='application/x-www-form-urlencoded',
body=urlencode({ body=str2bytes(urlencode({
'grant_type': 'refresh_token', 'grant_type': 'refresh_token',
'refresh_token': refresh_token, 'refresh_token': refresh_token,
}), })),
) )
self.assertEqual(status, 400) self.assertEqual(status, 400)
self.assertEqual(cookie_dict, {}) self.assertEqual(cookie_dict, {})
......
...@@ -51,7 +51,7 @@ from OFS.Traversable import NotFound ...@@ -51,7 +51,7 @@ from OFS.Traversable import NotFound
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.Timeout import getTimeLeft from Products.ERP5Type.Timeout import getTimeLeft
from Products.ERP5Type.Utils import bytes2str, str2bytes, str2unicode from Products.ERP5Type.Utils import bytes2str, unicode2str, str2bytes, str2unicode
from Products.ERP5Security.ERP5OAuth2ResourceServerPlugin import ( from Products.ERP5Security.ERP5OAuth2ResourceServerPlugin import (
OAuth2AuthorisationClientConnectorMixIn, OAuth2AuthorisationClientConnectorMixIn,
ERP5OAuth2ResourceServerPlugin, ERP5OAuth2ResourceServerPlugin,
...@@ -313,7 +313,7 @@ class _OAuth2AuthorisationServerProxy(object): ...@@ -313,7 +313,7 @@ class _OAuth2AuthorisationServerProxy(object):
def getAccessTokenSignatureAlgorithmAndPublicKeyList(self): def getAccessTokenSignatureAlgorithmAndPublicKeyList(self):
return tuple( return tuple(
(signature_algorithm.encode('ascii'), public_key.encode('ascii')) (unicode2str(signature_algorithm), unicode2str(public_key))
for signature_algorithm, public_key in self._queryERP5( for signature_algorithm, public_key in self._queryERP5(
'getAccessTokenSignatureAlgorithmAndPublicKeyList', 'getAccessTokenSignatureAlgorithmAndPublicKeyList',
) )
...@@ -882,7 +882,7 @@ class OAuth2AuthorisationClientConnector( ...@@ -882,7 +882,7 @@ class OAuth2AuthorisationClientConnector(
came_from = state_dict.get(_STATE_CAME_FROM_NAME) came_from = state_dict.get(_STATE_CAME_FROM_NAME)
if came_from: if came_from:
context = self # whatever context = self # whatever
kw['redirect_url'] = came_from.encode('utf-8') kw['redirect_url'] = unicode2str(came_from)
else: else:
context = self._getNeutralContextValue() context = self._getNeutralContextValue()
context.Base_redirect(**kw) context.Base_redirect(**kw)
...@@ -930,7 +930,7 @@ class OAuth2AuthorisationClientConnector( ...@@ -930,7 +930,7 @@ class OAuth2AuthorisationClientConnector(
REQUEST=REQUEST, REQUEST=REQUEST,
RESPONSE=RESPONSE, RESPONSE=RESPONSE,
) )
identifier_from_state = state_dict[_STATE_IDENTIFIER_NAME].encode('ascii') identifier_from_state = unicode2str(state_dict[_STATE_IDENTIFIER_NAME])
for ( for (
state_cookie_name, state_cookie_name,
identifier_from_cookie, identifier_from_cookie,
...@@ -965,7 +965,7 @@ class OAuth2AuthorisationClientConnector( ...@@ -965,7 +965,7 @@ class OAuth2AuthorisationClientConnector(
'code': code, 'code': code,
'redirect_uri': self.getRedirectUri(), 'redirect_uri': self.getRedirectUri(),
'client_id': self.getReference(), 'client_id': self.getReference(),
'code_verifier': state_dict[_STATE_CODE_VERIFIER_NAME].encode('ascii'), 'code_verifier': unicode2str(state_dict[_STATE_CODE_VERIFIER_NAME])
}, },
) )
access_token, _, error_message = self._setCookieFromTokenResponse( access_token, _, error_message = self._setCookieFromTokenResponse(
......
...@@ -48,6 +48,7 @@ from Products.PluggableAuthService.interfaces.plugins import ( ...@@ -48,6 +48,7 @@ from Products.PluggableAuthService.interfaces.plugins import (
) )
from Products.ERP5Security import _setUserNameForAccessLog from Products.ERP5Security import _setUserNameForAccessLog
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import bytes2str, str2bytes, unicode2str
# Public constants. Must not change once deployed. # Public constants. Must not change once deployed.
...@@ -109,11 +110,11 @@ def encodeAccessTokenPayload(payload): ...@@ -109,11 +110,11 @@ def encodeAccessTokenPayload(payload):
Encode given json-safe value into a format suitable for Encode given json-safe value into a format suitable for
decodeAccessTokenPayload. decodeAccessTokenPayload.
""" """
return base64.urlsafe_b64encode( return bytes2str(base64.urlsafe_b64encode(
zlib.compress( zlib.compress(
json.dumps(payload), str2bytes(json.dumps(payload)),
), ),
) ))
def decodeAccessTokenPayload(encoded_payload): def decodeAccessTokenPayload(encoded_payload):
""" """
...@@ -353,7 +354,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin): ...@@ -353,7 +354,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
if client_id is None: if client_id is None:
# Peek into token (without checking its signature) to guess the client_id # Peek into token (without checking its signature) to guess the client_id
# to look for. # to look for.
client_id = jwt.decode( client_id = unicode2str(jwt.decode(
raw_token, raw_token,
# no key. # no key.
# any algorithm is fine. # any algorithm is fine.
...@@ -361,7 +362,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin): ...@@ -361,7 +362,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
'verify_signature': False, 'verify_signature': False,
'verify_exp': False, 'verify_exp': False,
}, },
)['iss'].encode('utf-8') )['iss'])
assert client_id is not None assert client_id is not None
web_service_value_list = list(self.__iterClientConnectorValue( web_service_value_list = list(self.__iterClientConnectorValue(
client_id=client_id, client_id=client_id,
...@@ -440,9 +441,9 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin): ...@@ -440,9 +441,9 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
return return
# JWT is known valid. Access its content. # JWT is known valid. Access its content.
token_payload = decodeAccessTokenPayload( token_payload = decodeAccessTokenPayload(
token[JWT_PAYLOAD_KEY].encode('ascii'), bytes2str(token[JWT_PAYLOAD_KEY].encode('ascii')),
) )
client_id = token['iss'].encode('utf-8') client_id = unicode2str(token['iss'])
if self.__getWebServiceValue( if self.__getWebServiceValue(
client_id=client_id, client_id=client_id,
).getSessionVersion( ).getSessionVersion(
...@@ -452,8 +453,8 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin): ...@@ -452,8 +453,8 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
return return
return { return {
_PRIVATE_EXTRACTED_KEY: ( _PRIVATE_EXTRACTED_KEY: (
token_payload[JWT_PAYLOAD_USER_ID_KEY].encode('utf-8'), unicode2str(token_payload[JWT_PAYLOAD_USER_ID_KEY]),
token_payload[JWT_PAYLOAD_USER_CAPTION_KEY].encode('utf-8'), unicode2str(token_payload[JWT_PAYLOAD_USER_CAPTION_KEY]),
), ),
_PRIVATE_TOKEN_KEY: (access_token, refresh_token), _PRIVATE_TOKEN_KEY: (access_token, refresh_token),
_PRIVATE_CLIENT_ID: client_id, _PRIVATE_CLIENT_ID: client_id,
...@@ -467,10 +468,10 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin): ...@@ -467,10 +468,10 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
] or '', ] or '',
}, },
_PRIVATE_GROUP_LIST_KEY: tuple( _PRIVATE_GROUP_LIST_KEY: tuple(
x.encode('utf-8') for x in token_payload[JWT_PAYLOAD_GROUP_LIST_KEY] unicode2str(x) for x in token_payload[JWT_PAYLOAD_GROUP_LIST_KEY]
), ),
_PRIVATE_ROLE_LIST_KEY: tuple( _PRIVATE_ROLE_LIST_KEY: tuple(
x.encode('utf-8') for x in token_payload[JWT_PAYLOAD_ROLE_LIST_KEY] unicode2str(x) for x in token_payload[JWT_PAYLOAD_ROLE_LIST_KEY]
), ),
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment