diff --git a/fastapi_encrypted_errors/README.rst b/fastapi_encrypted_errors/README.rst new file mode 100644 index 000000000..271670f83 --- /dev/null +++ b/fastapi_encrypted_errors/README.rst @@ -0,0 +1,101 @@ +======================== +FastAPI Encrypted Errors +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:15dc8276c61dfb1e08999782de2a451f1d9531ada4979f1d4e02ae39e873406c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github + :target: https://github.com/OCA/rest-framework/tree/18.0/fastapi_encrypted_errors + :alt: OCA/rest-framework +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/rest-framework-18-0/rest-framework-18-0-fastapi_encrypted_errors + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a "ref" field in the error response of FastAPI. This +field is an AES encrypted string that contains the error message / +traceback. This encrypted string can be decrypted using the endpoint +decrypt error wizard. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +First you have to enable the encryption for an endpoint by checking the +Encrypt Errors checkbox in the endpoint configuration. + +To decrypt an error message, you can use the "Decrypt Error" wizard in +the FastAPI menu. + +You can regenerate a new key by clicking on the "Regenerate Key" button +next to the Errors Secret Key field. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Akretion + +Contributors +------------ + +- `Akretion `__: + + - Florian Mounier + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px + :target: https://github.com/paradoxxxzero + :alt: paradoxxxzero + +Current `maintainer `__: + +|maintainer-paradoxxxzero| + +This module is part of the `OCA/rest-framework `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fastapi_encrypted_errors/__init__.py b/fastapi_encrypted_errors/__init__.py new file mode 100644 index 000000000..d7f65dfa1 --- /dev/null +++ b/fastapi_encrypted_errors/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import wizards +from . import fastapi_dispatcher diff --git a/fastapi_encrypted_errors/__manifest__.py b/fastapi_encrypted_errors/__manifest__.py new file mode 100644 index 000000000..dac7c4e74 --- /dev/null +++ b/fastapi_encrypted_errors/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "FastAPI Encrypted Errors", + "summary": "Adds encrypted error messages to FastAPI error responses.", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Akretion,Odoo Community Association (OCA)", + "maintainers": ["paradoxxxzero"], + "website": "https://github.com/OCA/rest-framework", + "depends": [ + "fastapi", + ], + "data": [ + "security/ir.model.access.csv", + "views/fastapi_endpoint_views.xml", + "wizards/wizard_fastapi_decrypt_errors_views.xml", + ], + "demo": [], + "external_dependencies": { + "python": ["cryptography"], + }, +} diff --git a/fastapi_encrypted_errors/fastapi_dispatcher.py b/fastapi_encrypted_errors/fastapi_dispatcher.py new file mode 100644 index 000000000..a3f0524a2 --- /dev/null +++ b/fastapi_encrypted_errors/fastapi_dispatcher.py @@ -0,0 +1,35 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.http import _dispatchers + +from odoo.addons.fastapi.error_handlers import convert_exception_to_status_body +from odoo.addons.fastapi.fastapi_dispatcher import ( + FastApiDispatcher as BaseFastApiDispatcher, +) + + +# Inherit from last registered fastapi dispatcher +# This handles multiple overload of dispatchers +class FastApiDispatcher(_dispatchers.get("fastapi", BaseFastApiDispatcher)): + routing_type = "fastapi" + + def handle_error(self, exc): + environ = self._get_environ() + root_path = "/" + environ["PATH_INFO"].split("/")[1] + fastapi_endpoint = ( + self.request.env["fastapi.endpoint"] + .sudo() + .search([("root_path", "=", root_path)]) + ) + if fastapi_endpoint.encrypt_errors: + headers = getattr(exc, "headers", None) + status_code, body = convert_exception_to_status_body(exc) + if body: + body["ref"] = fastapi_endpoint._encrypt_error(exc) + return self.request.make_json_response( + body, status=status_code, headers=headers + ) + + return super().handle_error(exc) diff --git a/fastapi_encrypted_errors/i18n/fastapi_encrypted_errors.pot b/fastapi_encrypted_errors/i18n/fastapi_encrypted_errors.pot new file mode 100644 index 000000000..c15568724 --- /dev/null +++ b/fastapi_encrypted_errors/i18n/fastapi_encrypted_errors.pot @@ -0,0 +1,113 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fastapi_encrypted_errors +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: fastapi_encrypted_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_view_form +msgid "Close" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__create_uid +msgid "Created by" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__create_date +msgid "Created on" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.actions.act_window,name:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_action_decrypt_error +#: model:ir.ui.menu,name:fastapi_encrypted_errors.menu_fastapi_decrypt_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_view_form +msgid "Decrypt Error" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__decrypted_error +msgid "Decrypted Error" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__display_name +msgid "Display Name" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_fastapi_endpoint__encrypt_errors +msgid "Encrypt Errors" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,help:fastapi_encrypted_errors.field_fastapi_endpoint__encrypt_errors +msgid "Encrypt errors before sending them to the client." +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_fastapi_endpoint__encrypted_errors_secret_key +msgid "Encrypted Errors Secret Key" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__error +msgid "Error" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model,name:fastapi_encrypted_errors.model_fastapi_endpoint +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__fastapi_endpoint_id +msgid "FastAPI Endpoint" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.fastapi_endpoint_form_view +msgid "Generate Secret Key" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__id +msgid "ID" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_view_form +msgid "Label" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors____last_update +msgid "Last Modified on" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__write_date +msgid "Last Updated on" +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,help:fastapi_encrypted_errors.field_fastapi_endpoint__encrypted_errors_secret_key +msgid "" +"The secret key used to encrypt errors before sending them to the client." +msgstr "" + +#. module: fastapi_encrypted_errors +#: model:ir.model,name:fastapi_encrypted_errors.model_wizard_fastapi_decrypt_errors +msgid "Wizard to decrypt FastAPI errors" +msgstr "" diff --git a/fastapi_encrypted_errors/i18n/it.po b/fastapi_encrypted_errors/i18n/it.po new file mode 100644 index 000000000..1dea61661 --- /dev/null +++ b/fastapi_encrypted_errors/i18n/it.po @@ -0,0 +1,118 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fastapi_encrypted_errors +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-06-04 16:26+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: fastapi_encrypted_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_view_form +msgid "Close" +msgstr "Chiudi" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: fastapi_encrypted_errors +#: model:ir.actions.act_window,name:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_action_decrypt_error +#: model:ir.ui.menu,name:fastapi_encrypted_errors.menu_fastapi_decrypt_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_view_form +msgid "Decrypt Error" +msgstr "Decifra errore" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__decrypted_error +msgid "Decrypted Error" +msgstr "Errore decifrato" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_fastapi_endpoint__encrypt_errors +msgid "Encrypt Errors" +msgstr "Errori cifrati" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,help:fastapi_encrypted_errors.field_fastapi_endpoint__encrypt_errors +msgid "Encrypt errors before sending them to the client." +msgstr "Cifra gli errori prima di inviarli al client." + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_fastapi_endpoint__encrypted_errors_secret_key +msgid "Encrypted Errors Secret Key" +msgstr "Chiave segreta errori cifrati" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__error +msgid "Error" +msgstr "Errore" + +#. module: fastapi_encrypted_errors +#: model:ir.model,name:fastapi_encrypted_errors.model_fastapi_endpoint +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__fastapi_endpoint_id +msgid "FastAPI Endpoint" +msgstr "Endpoint FastAPI" + +#. module: fastapi_encrypted_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.fastapi_endpoint_form_view +msgid "Generate Secret Key" +msgstr "Genera chiave segreta" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__id +msgid "ID" +msgstr "ID" + +#. module: fastapi_encrypted_errors +#: model_terms:ir.ui.view,arch_db:fastapi_encrypted_errors.wizard_fastapi_decrypt_errors_view_form +msgid "Label" +msgstr "Etichetta" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,field_description:fastapi_encrypted_errors.field_wizard_fastapi_decrypt_errors__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: fastapi_encrypted_errors +#: model:ir.model.fields,help:fastapi_encrypted_errors.field_fastapi_endpoint__encrypted_errors_secret_key +msgid "" +"The secret key used to encrypt errors before sending them to the client." +msgstr "" +"La chiave segreta utilizzata per cifrare gli errori prima di inviarli al " +"client." + +#. module: fastapi_encrypted_errors +#: model:ir.model,name:fastapi_encrypted_errors.model_wizard_fastapi_decrypt_errors +msgid "Wizard to decrypt FastAPI errors" +msgstr "Procedura guidata per decifrare gli errori FastAPI" diff --git a/fastapi_encrypted_errors/models/__init__.py b/fastapi_encrypted_errors/models/__init__.py new file mode 100644 index 000000000..b825fab92 --- /dev/null +++ b/fastapi_encrypted_errors/models/__init__.py @@ -0,0 +1 @@ +from . import fastapi_endpoint diff --git a/fastapi_encrypted_errors/models/fastapi_endpoint.py b/fastapi_encrypted_errors/models/fastapi_endpoint.py new file mode 100644 index 000000000..01bf25637 --- /dev/null +++ b/fastapi_encrypted_errors/models/fastapi_endpoint.py @@ -0,0 +1,46 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import traceback +import zlib + +from cryptography.fernet import Fernet + +from odoo import fields, models + + +class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" + + encrypt_errors = fields.Boolean( + help="Encrypt errors before sending them to the client.", + ) + encrypted_errors_secret_key = fields.Char( + help="The secret key used to encrypt errors before sending them to the client.", + default=lambda _: Fernet.generate_key(), + ) + + def action_generate_encrypted_errors_secret_key(self): + for record in self: + record.encrypted_errors_secret_key = Fernet.generate_key() + + def _encrypt_error(self, exc): + self.ensure_one() + if not self.encrypt_errors or not self.encrypted_errors_secret_key: + return + + # Get full traceback + error = "".join(traceback.format_exception(exc)) + # zlib compression works quite well on tracebacks + error = zlib.compress(error.encode("utf-8")) + f = Fernet(self.encrypted_errors_secret_key) + return f.encrypt(error) + + def _decrypt_error(self, error): + self.ensure_one() + if not self.encrypt_errors or not self.encrypted_errors_secret_key: + return + + f = Fernet(self.encrypted_errors_secret_key) + error = f.decrypt(error) + return zlib.decompress(error).decode("utf-8") diff --git a/fastapi_encrypted_errors/pyproject.toml b/fastapi_encrypted_errors/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/fastapi_encrypted_errors/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fastapi_encrypted_errors/readme/CONTRIBUTORS.md b/fastapi_encrypted_errors/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..3b52e04e6 --- /dev/null +++ b/fastapi_encrypted_errors/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Akretion](https://www.akretion.com): + - Florian Mounier diff --git a/fastapi_encrypted_errors/readme/DESCRIPTION.md b/fastapi_encrypted_errors/readme/DESCRIPTION.md new file mode 100644 index 000000000..a296a0446 --- /dev/null +++ b/fastapi_encrypted_errors/readme/DESCRIPTION.md @@ -0,0 +1,4 @@ +This module adds a "ref" field in the error response of FastAPI. This +field is an AES encrypted string that contains the error message / +traceback. This encrypted string can be decrypted using the endpoint +decrypt error wizard. diff --git a/fastapi_encrypted_errors/readme/USAGE.md b/fastapi_encrypted_errors/readme/USAGE.md new file mode 100644 index 000000000..ad5af09f2 --- /dev/null +++ b/fastapi_encrypted_errors/readme/USAGE.md @@ -0,0 +1,8 @@ +First you have to enable the encryption for an endpoint by checking the +Encrypt Errors checkbox in the endpoint configuration. + +To decrypt an error message, you can use the "Decrypt Error" wizard in +the FastAPI menu. + +You can regenerate a new key by clicking on the "Regenerate Key" button +next to the Errors Secret Key field. diff --git a/fastapi_encrypted_errors/security/ir.model.access.csv b/fastapi_encrypted_errors/security/ir.model.access.csv new file mode 100644 index 000000000..d102f0c82 --- /dev/null +++ b/fastapi_encrypted_errors/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_fastapi_wizard_auth_partner_impersonate,wizard_fastapi_decrypt_errors,model_wizard_fastapi_decrypt_errors,fastapi.group_fastapi_manager,1,1,1,1 diff --git a/fastapi_encrypted_errors/static/description/icon.png b/fastapi_encrypted_errors/static/description/icon.png new file mode 100644 index 000000000..1dcc49c24 Binary files /dev/null and b/fastapi_encrypted_errors/static/description/icon.png differ diff --git a/fastapi_encrypted_errors/static/description/index.html b/fastapi_encrypted_errors/static/description/index.html new file mode 100644 index 000000000..6f0ff11fe --- /dev/null +++ b/fastapi_encrypted_errors/static/description/index.html @@ -0,0 +1,441 @@ + + + + + +FastAPI Encrypted Errors + + + +
+

FastAPI Encrypted Errors

+ + +

Beta License: AGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

+

This module adds a “ref” field in the error response of FastAPI. This +field is an AES encrypted string that contains the error message / +traceback. This encrypted string can be decrypted using the endpoint +decrypt error wizard.

+

Table of contents

+ +
+

Usage

+

First you have to enable the encryption for an endpoint by checking the +Encrypt Errors checkbox in the endpoint configuration.

+

To decrypt an error message, you can use the “Decrypt Error” wizard in +the FastAPI menu.

+

You can regenerate a new key by clicking on the “Regenerate Key” button +next to the Errors Secret Key field.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

paradoxxxzero

+

This module is part of the OCA/rest-framework project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/fastapi_encrypted_errors/tests/__init__.py b/fastapi_encrypted_errors/tests/__init__.py new file mode 100644 index 000000000..8fe3d60a6 --- /dev/null +++ b/fastapi_encrypted_errors/tests/__init__.py @@ -0,0 +1 @@ +from . import test_fastapi_encrypted_errors diff --git a/fastapi_encrypted_errors/tests/test_fastapi_encrypted_errors.py b/fastapi_encrypted_errors/tests/test_fastapi_encrypted_errors.py new file mode 100644 index 000000000..21a722cf5 --- /dev/null +++ b/fastapi_encrypted_errors/tests/test_fastapi_encrypted_errors.py @@ -0,0 +1,78 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import os +import unittest + +from odoo.tests.common import HttpCase +from odoo.tools import mute_logger + +from odoo.addons.fastapi.schemas import DemoExceptionType + +from fastapi import status + + +@unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "FastAPIEncryptedErrorsCase skipped") +class FastAPIEncryptedErrorsCase(HttpCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.fastapi_demo_app = cls.env.ref("fastapi.fastapi_endpoint_demo") + cls.fastapi_demo_app._handle_registry_sync() + cls.fastapi_demo_app.write({"encrypt_errors": True}) + lang = ( + cls.env["res.lang"] + .with_context(active_test=False) + .search([("code", "=", "fr_BE")]) + ) + lang.active = True + + @mute_logger("odoo.http", "odoo.addons.base.models.assetsbundle") + def test_encrypted_errors_in_response(self): + route = ( + "/fastapi_demo/demo/exception?" + f"exception_type={DemoExceptionType.user_error.value}" + "&error_message=User Error" + ) + response = self.url_open(route, timeout=200) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + res = response.json() + self.assertEqual(res["detail"], "User Error") + self.assertIn("ref", res) + + route = ( + "/fastapi_demo/demo/exception?" + f"exception_type={DemoExceptionType.bare_exception.value}" + "&error_message=Internal Server Error" + ) + response = self.url_open(route, timeout=200) + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + res = response.json() + self.assertEqual(res["detail"], "Internal Server Error") + self.assertIn("ref", res) + + @mute_logger("odoo.http", "odoo.addons.base.models.assetsbundle") + def test_encrypted_errors_decrypt(self): + route = ( + "/fastapi_demo/demo/exception?" + f"exception_type={DemoExceptionType.bare_exception.value}" + "&error_message=Internal Server Error" + ) + response = self.url_open(route, timeout=200) + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + res = response.json() + self.assertEqual(res["detail"], "Internal Server Error") + self.assertIn("ref", res) + ref = res["ref"] + self.assertNotIn("Traceback (most recent call last)", ref) + self.assertNotIn("NotImplementedError: Internal Server Error", ref) + + wizard = self.env["wizard.fastapi.decrypt.errors"].create({"error": ref}) + wizard.action_decrypt_error() + self.assertIn("Traceback (most recent call last)", wizard.decrypted_error) + self.assertIn( + "NotImplementedError: Internal Server Error", wizard.decrypted_error + ) diff --git a/fastapi_encrypted_errors/views/fastapi_endpoint_views.xml b/fastapi_encrypted_errors/views/fastapi_endpoint_views.xml new file mode 100644 index 000000000..5b2be3f44 --- /dev/null +++ b/fastapi_encrypted_errors/views/fastapi_endpoint_views.xml @@ -0,0 +1,31 @@ + + + + + fastapi.endpoint + + + + +
+
+
+
+
+
diff --git a/fastapi_encrypted_errors/wizards/__init__.py b/fastapi_encrypted_errors/wizards/__init__.py new file mode 100644 index 000000000..a160dabbb --- /dev/null +++ b/fastapi_encrypted_errors/wizards/__init__.py @@ -0,0 +1 @@ +from . import wizard_fastapi_decrypt_errors diff --git a/fastapi_encrypted_errors/wizards/wizard_fastapi_decrypt_errors.py b/fastapi_encrypted_errors/wizards/wizard_fastapi_decrypt_errors.py new file mode 100644 index 000000000..d20bfb849 --- /dev/null +++ b/fastapi_encrypted_errors/wizards/wizard_fastapi_decrypt_errors.py @@ -0,0 +1,40 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import traceback + +from odoo import fields, models + + +class WizardFastapiDecryptErrors(models.TransientModel): + _name = "wizard.fastapi.decrypt.errors" + _description = "Wizard to decrypt FastAPI errors" + + error = fields.Text(required=True) + fastapi_endpoint_id = fields.Many2one( + "fastapi.endpoint", + string="FastAPI Endpoint", + required=True, + default=lambda self: self.env["fastapi.endpoint"].search([], limit=1), + ) + decrypted_error = fields.Text() + + def action_decrypt_error(self): + self.ensure_one() + try: + error = self.fastapi_endpoint_id._decrypt_error(self.error.encode("utf-8")) + except Exception: + self.decrypted_error = ( + "Error while decrypting error: \n\n" + traceback.format_exc() + ) + else: + self.decrypted_error = error + + return { + "type": "ir.actions.act_window", + "res_model": "wizard.fastapi.decrypt.errors", + "view_mode": "form", + "view_type": "form", + "res_id": self.id, + "target": "new", + } diff --git a/fastapi_encrypted_errors/wizards/wizard_fastapi_decrypt_errors_views.xml b/fastapi_encrypted_errors/wizards/wizard_fastapi_decrypt_errors_views.xml new file mode 100644 index 000000000..171f33c72 --- /dev/null +++ b/fastapi_encrypted_errors/wizards/wizard_fastapi_decrypt_errors_views.xml @@ -0,0 +1,44 @@ + + + + wizard.fastapi.decrypt.errors + +
+ + + + + +
+
+
+
+
+ + + Decrypt Error + wizard.fastapi.decrypt.errors + ir.actions.act_window + form + new + + + + +