From 79d29c51dfa6a16434bd87ffb76a0970daf85019 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 14 Nov 2018 08:33:30 +0000 Subject: [PATCH] [FIX] base_import_module, cli: deploy The use of the `deploy` command always fails because of an incorrect CSRF token since commit 9bae56acd4ebe2b1. Indeed, the latter re-introduces the session rotation, i.e. the session ID is changed at authentication. Practically, what happens server-side is: - authentication - generate CSRF token - create the response with the token and a change of session ID At this point, the token generated is not correct anymore since it is based on the 'old' session ID. Therefore, when it is reused at uploading, an error is raised. It is actually possible to simplify the process by performing the authentication and the file upload in a single request. There is indeed no real use of extracting the authentication, since the request is then only used to upload the module. opw-1902863 closes odoo/odoo#28653 --- addons/base_import_module/controllers/main.py | 16 ++------ odoo/cli/deploy.py | 39 +++++++------------ 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/addons/base_import_module/controllers/main.py b/addons/base_import_module/controllers/main.py index ca76cd53b8e..d382d360961 100644 --- a/addons/base_import_module/controllers/main.py +++ b/addons/base_import_module/controllers/main.py @@ -24,22 +24,14 @@ class ImportModule(Controller): if not is_admin: raise AccessError(_("Only administrators can upload a module")) - @route('/base_import_module/login', type='http', auth='none', methods=['POST'], csrf=False) + @route( + '/base_import_module/login_upload', + type='http', auth='none', methods=['POST'], csrf=False, save_session=False) @webservice - def login(self, login, password, db=None): + def login_upload(self, login, password, db=None, force='', mod_file=None, **kw): if db and db != request.db: raise Exception(_("Could not select database '%s'") % db) uid = request.session.authenticate(request.db, login, password) - if not uid: - return Response(response="Wrong login/password", status=401) self.check_user(uid) - return Response(headers={ - 'X-CSRF-TOKEN': request.csrf_token(), - }) - - @route('/base_import_module/upload', type='http', auth='user', methods=['POST']) - @webservice - def upload(self, mod_file=None, force='', **kw): - self.check_user() force = True if force == '1' else False return request.env['ir.module.module'].import_zipfile(mod_file, force=force)[0] diff --git a/odoo/cli/deploy.py b/odoo/cli/deploy.py index 6e76cea7d45..29b969135dd 100644 --- a/odoo/cli/deploy.py +++ b/odoo/cli/deploy.py @@ -18,40 +18,29 @@ class Deploy(Command): def deploy_module(self, module_path, url, login, password, db='', force=False): url = url.rstrip('/') - csrf_token = self.authenticate(url, login, password, db) module_file = self.zip_module(module_path) try: - return self.upload_module(url, module_file, force=force, csrf_token=csrf_token) + return self.login_upload_module(module_file, url, login, password, db, force=force) finally: os.remove(module_file) - def upload_module(self, server, module_file, force=False, csrf_token=None): + def login_upload_module(self, module_file, url, login, password, db, force=False): print("Uploading module file...") - url = server + '/base_import_module/upload' - - post_data = {'force': '1' if force else ''} - if csrf_token: post_data['csrf_token'] = csrf_token - + endpoint = url + '/base_import_module/login_upload' + post_data = { + 'login': login, + 'password': password, + 'db': db, + 'force': '1' if force else '', + } with open(module_file, 'rb') as f: - res = self.session.post(url, files={'mod_file': f}, data=post_data) - res.raise_for_status() + res = self.session.post(endpoint, files={'mod_file': f}, data=post_data) - return res.text - - def authenticate(self, server, login, password, db=''): - print("Authenticating on server '%s' ..." % server) - - # Fixate session with a given db if any - self.session.get(server + '/web/login', params=dict(db=db)) - - args = dict(login=login, password=password, db=db) - res = self.session.post(server + '/base_import_module/login', args) if res.status_code == 404: - raise Exception("The server '%s' does not have the 'base_import_module' installed." % server) - elif res.status_code != 200: - raise Exception(res.text) - - return res.headers.get('x-csrf-token') + raise Exception( + "The server '%s' does not have the 'base_import_module' installed or is not up-to-date." % url) + res.raise_for_status() + return res.text def zip_module(self, path): path = os.path.abspath(path)