From b4c50708de97ac6abc0776cac738b26bc38e70e9 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 Feb 2023 21:40:09 +0000 Subject: [PATCH] [FIX] new request management in V16.0 --- galicea_openid_connect/api.py | 23 +++-- .../controllers/ext_web_login.py | 7 +- galicea_openid_connect/controllers/main.py | 90 ++++++++++--------- 3 files changed, 64 insertions(+), 56 deletions(-) diff --git a/galicea_openid_connect/api.py b/galicea_openid_connect/api.py index 8b33e40..6b17fec 100644 --- a/galicea_openid_connect/api.py +++ b/galicea_openid_connect/api.py @@ -5,6 +5,7 @@ import logging from functools import wraps from odoo import http +from odoo.http import request import werkzeug _logger = logging.getLogger(__name__) @@ -22,13 +23,13 @@ def resource(path, method, auth='user'): def endpoint_decorator(func): @http.route(path, auth='public', type='http', methods=[method, 'OPTIONS'], csrf=False) @wraps(func) - def func_wrapper(self, req, **query): + def func_wrapper(self, **query): cors_headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode, Authorization', 'Access-Control-Max-Age': 60 * 60 * 24, } - if req.httprequest.method == 'OPTIONS': + if request.httprequest.method == 'OPTIONS': return http.Response( status=200, headers=cors_headers @@ -36,8 +37,8 @@ def resource(path, method, auth='user'): try: access_token = None - if 'Authorization' in req.httprequest.headers: - authorization_header = req.httprequest.headers['Authorization'] + if 'Authorization' in request.httprequest.headers: + authorization_header = request.httprequest.headers['Authorization'] if authorization_header[:7] == 'Bearer ': access_token = authorization_header.split(' ', 1)[1] if access_token is None: @@ -48,7 +49,7 @@ def resource(path, method, auth='user'): 'invalid_request', ) if auth == 'user': - token = req.env['galicea_openid_connect.access_token'].sudo().search( + token = request.env['galicea_openid_connect.access_token'].sudo().search( [('token', '=', access_token)] ) if not token: @@ -56,9 +57,9 @@ def resource(path, method, auth='user'): 'access_token is invalid', 'invalid_request', ) - req.uid = token.user_id.id + request.update_env(user=token.user_id.id) elif auth == 'client': - token = req.env['galicea_openid_connect.client_access_token'].sudo().search( + token = request.env['galicea_openid_connect.client_access_token'].sudo().search( [('token', '=', access_token)] ) if not token: @@ -66,13 +67,11 @@ def resource(path, method, auth='user'): 'access_token is invalid', 'invalid_request', ) - req.uid = token.client_id.system_user_id.id + request.update_env(user=token.client_id.system_user_id.id) - ctx = req.context.copy() - ctx.update({'client_id': token.client_id.id}) - req.context = ctx + request.update_env(context={'client_id': token.client_id.id}) - response = func(self, req, **query) + response = func(self, **query) return werkzeug.Response( response=json.dumps(response), headers=cors_headers, diff --git a/galicea_openid_connect/controllers/ext_web_login.py b/galicea_openid_connect/controllers/ext_web_login.py index 60bcde9..6bf96ab 100644 --- a/galicea_openid_connect/controllers/ext_web_login.py +++ b/galicea_openid_connect/controllers/ext_web_login.py @@ -3,15 +3,16 @@ import time from odoo import http +from odoo.http import request from odoo.addons import web -class Home(web.controllers.main.Home): +class Home(web.controllers.home.Home): @http.route('/web/login', type='http', auth="none") def web_login(self, redirect=None, **kw): result = super(Home, self).web_login(redirect, **kw) if result.is_qweb and 'force_auth_and_redirect' in kw: result.qcontext['redirect'] = kw['force_auth_and_redirect'] - if http.request.params.get('login_success'): - http.request.session['auth_time'] = int(time.time()) + if request.params.get('login_success'): + request.session['auth_time'] = int(time.time()) return result diff --git a/galicea_openid_connect/controllers/main.py b/galicea_openid_connect/controllers/main.py index 4e3934e..b0f5063 100644 --- a/galicea_openid_connect/controllers/main.py +++ b/galicea_openid_connect/controllers/main.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import json +import logging import time import os import base64 @@ -8,6 +9,7 @@ import base64 from odoo import http from odoo.http import request import werkzeug +from werkzeug.urls import url_encode from .. api import resource @@ -18,6 +20,8 @@ try: except ImportError: pass +_logger = logging.getLogger(__name__) + def jwk_from_json(json_key): key = jwk.JWK() key.import_key(**json.loads(json_key)) @@ -54,24 +58,24 @@ class OAuthException(Exception): self.type = type class Main(http.Controller): - def __get_authorization_code_jwk(self, req): - return jwk_from_json(req.env['ir.config_parameter'].sudo().get_param( + def __get_authorization_code_jwk(self): + return jwk_from_json(request.env['ir.config_parameter'].sudo().get_param( 'galicea_openid_connect.authorization_code_jwk' )) - def __get_id_token_jwk(self, req): - return jwk_from_json(req.env['ir.config_parameter'].sudo().get_param( + def __get_id_token_jwk(self): + return jwk_from_json(request.env['ir.config_parameter'].sudo().get_param( 'galicea_openid_connect.id_token_jwk' )) - def __validate_client(self, req, **query): + def __validate_client(self, **query): if 'client_id' not in query: raise OAuthException( 'client_id param is missing', OAuthException.INVALID_CLIENT, ) client_id = query['client_id'] - client = req.env['galicea_openid_connect.client'].sudo().search( + client = request.env['galicea_openid_connect.client'].sudo().search( [('client_id', '=', client_id)] ) if not client: @@ -81,7 +85,7 @@ class Main(http.Controller): ) return client - def __validate_redirect_uri(self, client, req, **query): + def __validate_redirect_uri(self, client, **query): if 'redirect_uri' not in query: raise OAuthException( 'redirect_uri param is missing', @@ -97,7 +101,7 @@ class Main(http.Controller): return redirect_uri - def __validate_client_secret(self, client, req, **query): + def __validate_client_secret(self, client, **query): if 'client_secret' not in query or query['client_secret'] != client.secret: raise OAuthException( 'client_secret param is not valid', @@ -125,7 +129,7 @@ class Main(http.Controller): @http.route('/oauth/jwks', auth='public', type='http') def jwks(self, **query): keyset = jwk.JWKSet() - keyset.add(self.__get_id_token_jwk(request)) + keyset.add(self.__get_id_token_jwk()) return keyset.export(private_keys=False) @resource('/oauth/userinfo', method='GET') @@ -152,8 +156,8 @@ class Main(http.Controller): def authorize(self, **query): # First, validate client_id and redirect_uri params. try: - client = self.__validate_client(request, **query) - redirect_uri = self.__validate_redirect_uri(client, request, **query) + client = self.__validate_client(**query) + redirect_uri = self.__validate_redirect_uri(client, **query) except OAuthException as e: # If those are not valid, we must not redirect back to the client # - instead, we display a message to the user @@ -203,14 +207,14 @@ class Main(http.Controller): needs_login = True if needs_login: params = { - 'force_auth_and_redirect': '/oauth/authorize?{}'.format(werkzeug.url_encode(query)) + 'force_auth_and_redirect': '/oauth/authorize?{}'.format(url_encode(query)) } return self.__redirect('/web/login', params, 'query') response_types = response_type.split() extra_claims = { - 'sid': http.request.httprequest.session.sid, + 'sid': http.request.session.sid, } if 'nonce' in query: extra_claims['nonce'] = query['nonce'] @@ -228,7 +232,7 @@ class Main(http.Controller): 'exp': int(time.time()) + 60 } payload.update(extra_claims) - key = self.__get_authorization_code_jwk(request) + key = self.__get_authorization_code_jwk() response_params['code'] = jwt_encode(payload, key) if 'token' in response_types: access_token = request.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( @@ -244,23 +248,24 @@ class Main(http.Controller): #extra_claims['at_hash'] = base64.urlsafe_b64encode(at_hash[:16]).strip('=') extra_claims['at_hash'] = base64.urlsafe_b64encode(at_hash[:16]) if 'id_token' in response_types: - response_params['id_token'] = self.__create_id_token(request, user.id, client, extra_claims) + response_params['id_token'] = self.__create_id_token(user.id, client, extra_claims) return self.__redirect(redirect_uri, response_params, response_mode) @http.route('/oauth/token', auth='public', type='http', methods=['POST', 'OPTIONS'], csrf=False) - def token(self, req, **query): + def token(self, **query): cors_headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode, Authorization', 'Access-Control-Max-Age': 60 * 60 * 24, } - if req.httprequest.method == 'OPTIONS': + _logger.info("Test1 %s" % request.httprequest.method) + if request.httprequest.method == 'OPTIONS': return http.Response( status=200, headers=cors_headers ) - + _logger.info("Test2 %s" % query) try: if 'grant_type' not in query: raise OAuthException( @@ -268,12 +273,15 @@ class Main(http.Controller): OAuthException.INVALID_REQUEST, ) if query['grant_type'] == 'authorization_code': - return json.dumps(self.__handle_grant_type_authorization_code(req, **query)) + _logger.info("Test3") + return json.dumps(self.__handle_grant_type_authorization_code(**query)) elif query['grant_type'] == 'client_credentials': - return json.dumps(self.__handle_grant_type_client_credentials(req, **query)) + _logger.info("Test4") + return json.dumps(self.__handle_grant_type_client_credentials(**query)) elif query['grant_type'] == 'password': + _logger.info("Test5") return werkzeug.Response( - response=json.dumps(self.__handle_grant_type_password(req, **query)), + response=json.dumps(self.__handle_grant_type_password(**query)), headers=cors_headers ) else: @@ -285,10 +293,10 @@ class Main(http.Controller): body = json.dumps({'error': e.type, 'error_description': e}) return werkzeug.Response(response=body, status=400, headers=cors_headers) - def __handle_grant_type_authorization_code(self, req, **query): - client = self.__validate_client(req, **query) - redirect_uri = self.__validate_redirect_uri(client, req, **query) - self.__validate_client_secret(client, req, **query) + def __handle_grant_type_authorization_code(self, **query): + client = self.__validate_client(**query) + redirect_uri = self.__validate_redirect_uri(client, **query) + self.__validate_client_secret(client, **query) if 'code' not in query: raise OAuthException( @@ -296,7 +304,7 @@ class Main(http.Controller): OAuthException.INVALID_GRANT, ) try: - payload = jwt_decode(query['code'], self.__get_authorization_code_jwk(req)) + payload = jwt_decode(query['code'], self.__get_authorization_code_jwk()) except jwt.JWTExpired: raise OAuthException( 'Code expired', @@ -319,7 +327,7 @@ class Main(http.Controller): ) # Retrieve/generate access token. We currently only store one per user/client - token = req.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( + token = request.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( payload['user_id'], client.id ) @@ -329,11 +337,11 @@ class Main(http.Controller): } if 'openid' in payload['scopes']: extra_claims = { name: payload[name] for name in payload if name in ['sid', 'nonce'] } - response['id_token'] = self.__create_id_token(req, payload['user_id'], client, extra_claims) + response['id_token'] = self.__create_id_token(payload['user_id'], client, extra_claims) return response - def __handle_grant_type_password(self, req, **query): - client = self.__validate_client(req, **query) + def __handle_grant_type_password(self, **query): + client = self.__validate_client(**query) if not client.allow_password_grant: raise OAuthException( 'This client is not allowed to perform password flow', @@ -346,8 +354,8 @@ class Main(http.Controller): '{} is required'.format(param), OAuthException.INVALID_REQUEST ) - user_id = req.env['res.users'].authenticate( - req.env.cr.dbname, + user_id = request.env['res.users'].authenticate( + request.env.cr.dbname, query['username'], query['password'], None @@ -360,7 +368,7 @@ class Main(http.Controller): scopes = query['scope'].split(' ') if query.get('scope') else [] # Retrieve/generate access token. We currently only store one per user/client - token = req.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( + token = request.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( user_id, client.id ) @@ -369,19 +377,19 @@ class Main(http.Controller): 'token_type': 'bearer' } if 'openid' in scopes: - response['id_token'] = self.__create_id_token(req, user_id, client, {}) + response['id_token'] = self.__create_id_token(user_id, client, {}) return response - def __handle_grant_type_client_credentials(self, req, **query): - client = self.__validate_client(req, **query) - self.__validate_client_secret(client, req, **query) - token = req.env['galicea_openid_connect.client_access_token'].sudo().retrieve_or_create(client.id) + def __handle_grant_type_client_credentials(self, **query): + client = self.__validate_client(**query) + self.__validate_client_secret(client, **query) + token = request.env['galicea_openid_connect.client_access_token'].sudo().retrieve_or_create(client.id) return { 'access_token': token.token, 'token_type': 'bearer' } - def __create_id_token(self, req, user_id, client, extra_claims): + def __create_id_token(self, user_id, client, extra_claims): claims = { 'iss': http.request.httprequest.host_url, 'sub': str(user_id), @@ -397,14 +405,14 @@ class Main(http.Controller): if 'at_hash' in extra_claims: claims['at_hash'] = extra_claims['at_hash'] - key = self.__get_id_token_jwk(req) + key = self.__get_id_token_jwk() return jwt_encode(claims, key) def __redirect(self, url, params, response_mode): location = '{}{}{}'.format( url, '?' if response_mode == 'query' else '#', - werkzeug.url_encode(params) + url_encode(params) ) return werkzeug.Response( headers={'Location': location},