diff --git a/rpc_helper/README.rst b/rpc_helper/README.rst new file mode 100644 index 00000000000..eb5d4089bb4 --- /dev/null +++ b/rpc_helper/README.rst @@ -0,0 +1,139 @@ +=========== +Disable RPC +=========== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c19fd3c9f11f0161f86e314ba36731565c735edaae0052a305fc6ad9112b1fd9 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/14.0/rpc_helper + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-rpc_helper + :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/server-tools&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Provide helpers to authorize RPC calls. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Enable debug mode and go to "Technical -> Database Structure -> Models". + +Open the model that you like to configure and go to the tab "RPC config". + +There you see a text field which supports JSON configuration. + +The configuration is the same you can pass via decorator. +The only difference is that you have to wrap values in a dictionary +like `{"disable": [...values...]}`. + +To disable all calls:: + + { + "disable": ["all"], + } + +To disable only some methods:: + + { + "disable": ["create", "write", "another_method"], + } + +NOTE: on the resulting JSON will be automatically formatted on save for better readability. + +Usage +===== + +Via code +~~~~~~~~ + +Decorate an Odoo model class like this:: + + from odoo.addons.rpc_helper.decorator import disable_rpc + + @disable_rpc() + class AverageModel(models.Model): + _inherit = "avg.model" + +This will disable ALL calls. + +To selectively disable only some methods:: + + @disable_rpc("create", "write", "any_method") + class AverageModel(models.Model): + _inherit = "avg.model" + + +Via `ir.model` configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See "Configuration" section. + +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 +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Simone Orsi + +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-simahawk| image:: https://github.com/simahawk.png?size=40px + :target: https://github.com/simahawk + :alt: simahawk + +Current `maintainer `__: + +|maintainer-simahawk| + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/rpc_helper/__init__.py b/rpc_helper/__init__.py new file mode 100644 index 00000000000..5bf67492bc8 --- /dev/null +++ b/rpc_helper/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import post_load_hook diff --git a/rpc_helper/__manifest__.py b/rpc_helper/__manifest__.py new file mode 100644 index 00000000000..0ac6bd676cf --- /dev/null +++ b/rpc_helper/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "Disable RPC", + "summary": """Helpers for disabling RPC calls""", + "version": "12.0.1.0.0", + "development_status": "Beta", + "license": "LGPL-3", + "website": "https://github.com/OCA/server-tools", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["simahawk"], + "depends": ["base_sparse_field"], + "data": ["views/ir_model_views.xml"], + "post_load": "post_load_hook", +} diff --git a/rpc_helper/decorator.py b/rpc_helper/decorator.py new file mode 100644 index 00000000000..99c875c90a9 --- /dev/null +++ b/rpc_helper/decorator.py @@ -0,0 +1,19 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +def disable_rpc(*config): + """Decorate classes to disable RPC calls. + + Possible values: + + * none, block all methods + * *("$method_name1", "$method_name2"), blocks calls to specific methods + """ + + def _decorator(target): + target._disable_rpc = ("all",) if len(config) == 0 else config + return target + + return _decorator diff --git a/rpc_helper/hooks.py b/rpc_helper/hooks.py new file mode 100644 index 00000000000..e6dbad6dac6 --- /dev/null +++ b/rpc_helper/hooks.py @@ -0,0 +1,22 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import logging + +from odoo.service import model + +from .patch import protected__execute_cr + +_logger = logging.getLogger(__name__) + + +def patch__model_execute_cr(): + """Patch rpc model handler.""" + protected__execute_cr._orig__execute_cr = model.execute_cr + model.execute_cr = protected__execute_cr + _logger.info("PATCHED odoo.service.model.execute") + + +def post_load_hook(): + patch__model_execute_cr() diff --git a/rpc_helper/i18n/es.po b/rpc_helper/i18n/es.po new file mode 100644 index 00000000000..32032217e48 --- /dev/null +++ b/rpc_helper/i18n/es.po @@ -0,0 +1,106 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rpc_helper +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-04-21 22:42+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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 4.17\n" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids +msgid "Changeset Changes" +msgstr "Cambios en el Conjunto de Modificaciones" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids +msgid "Changesets" +msgstr "Conjuntos de Cambios" + +#. module: rpc_helper +#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit +msgid "" +"Configure RPC config via JSON. Value must be a list of methods to disable " +"wrapped by a dict with key `disable`. Eg: {'disable': ['search', " +"'do_this']}To disable all methods, use `{'disable: ['all']}`" +msgstr "" +"Configurar RPC vía JSON. El valor debe ser una lista de métodos a " +"deshabilitar envueltos por un dict con la clave `disable`. Ej: {'disable': [" +"'search', 'do_this']}Para deshabilitar todos los métodos, usa `{'disable: " +"['all']}`" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes +msgid "Count Pending Changeset Changes" +msgstr "Recuento de Cambios Pendientes" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets +msgid "Count Pending Changesets" +msgstr "Recuento de Cambios Pendientes" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__display_name +msgid "Display Name" +msgstr "Nombre para Mostrar" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__id +msgid "ID" +msgstr "ID (identificación)" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model____last_update +msgid "Last Modified on" +msgstr "Última Modificación el" + +#. module: rpc_helper +#: model:ir.model,name:rpc_helper.model_ir_model +msgid "Models" +msgstr "Modelos" + +#. module: rpc_helper +#: code:addons/rpc_helper/patch.py:0 +#, python-format +msgid "Object %s doesn't exist" +msgstr "El objeto %s no existe" + +#. module: rpc_helper +#: code:addons/rpc_helper/patch.py:0 +#, python-format +msgid "RPC call on %s is not allowed" +msgstr "La llamada RPC en %s no está permitida" + +#. module: rpc_helper +#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form +msgid "RPC config" +msgstr "Configuración RPC" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config +msgid "Rpc Config" +msgstr "Configuración Rpc" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit +msgid "Rpc Config Edit" +msgstr "Editar configuración Rpc" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search +msgid "Smart Search" +msgstr "Búsqueda Inteligente" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset +msgid "User Can See Changeset" +msgstr "Usuario Puede ver Conjunto de Cambios" diff --git a/rpc_helper/i18n/es_AR.po b/rpc_helper/i18n/es_AR.po new file mode 100644 index 00000000000..9d81aba2c18 --- /dev/null +++ b/rpc_helper/i18n/es_AR.po @@ -0,0 +1,106 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rpc_helper +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-09-02 16:07+0000\n" +"Last-Translator: Ignacio Buioli \n" +"Language-Team: none\n" +"Language: es_AR\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 4.3.2\n" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids +msgid "Changeset Changes" +msgstr "Cambios del Conjunto de Cambios" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids +msgid "Changesets" +msgstr "Conjunto de Cambios" + +#. module: rpc_helper +#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit +msgid "" +"Configure RPC config via JSON. Value must be a list of methods to disable " +"wrapped by a dict with key `disable`. Eg: {'disable': ['search', 'do_this']}" +"To disable all methods, use `{'disable: ['all']}`" +msgstr "" +"Configure los ajustes de RPC a través de JSON. El valor debe ser una lista " +"de métodos para deshabilitar envueltos por un dict con la clave " +"`deshabilitar`. Por ejemplo: {'disable': ['search', 'do_this']} Para " +"deshabilitar todos los métodos, use `{'disable: ['all']}`" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes +msgid "Count Pending Changeset Changes" +msgstr "Cuenta de los Cambios Pendientes de los Conjuntos de Cambios" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets +msgid "Count Pending Changesets" +msgstr "Cuenta de los Conjuntos de Cambios Pendientes" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__id +msgid "ID" +msgstr "ID" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: rpc_helper +#: model:ir.model,name:rpc_helper.model_ir_model +msgid "Models" +msgstr "Modelos" + +#. module: rpc_helper +#: code:addons/rpc_helper/patch.py:0 +#, python-format +msgid "Object %s doesn't exist" +msgstr "El Objeto %s no existe" + +#. module: rpc_helper +#: code:addons/rpc_helper/patch.py:0 +#, python-format +msgid "RPC call on %s is not allowed" +msgstr "Las llamadas RPC en %s no están permitidas" + +#. module: rpc_helper +#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form +msgid "RPC config" +msgstr "Configuración RPC" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config +msgid "Rpc Config" +msgstr "Configuración Rpc" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit +msgid "Rpc Config Edit" +msgstr "Editar la Configuración Rpc" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search +msgid "Smart Search" +msgstr "Búsqueda Inteligente" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset +msgid "User Can See Changeset" +msgstr "El Usuario Puede ver Conjuntos de Cambios" diff --git a/rpc_helper/i18n/rpc_helper.pot b/rpc_helper/i18n/rpc_helper.pot new file mode 100644 index 00000000000..060291fa582 --- /dev/null +++ b/rpc_helper/i18n/rpc_helper.pot @@ -0,0 +1,99 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rpc_helper +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.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: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids +msgid "Changeset Changes" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids +msgid "Changesets" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit +msgid "" +"Configure RPC config via JSON. Value must be a list of methods to disable " +"wrapped by a dict with key `disable`. Eg: {'disable': ['search', " +"'do_this']}To disable all methods, use `{'disable: ['all']}`" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes +msgid "Count Pending Changeset Changes" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets +msgid "Count Pending Changesets" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__display_name +msgid "Display Name" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__id +msgid "ID" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model____last_update +msgid "Last Modified on" +msgstr "" + +#. module: rpc_helper +#: model:ir.model,name:rpc_helper.model_ir_model +msgid "Models" +msgstr "" + +#. module: rpc_helper +#: code:addons/rpc_helper/patch.py:0 +#, python-format +msgid "Object %s doesn't exist" +msgstr "" + +#. module: rpc_helper +#: code:addons/rpc_helper/patch.py:0 +#, python-format +msgid "RPC call on %s is not allowed" +msgstr "" + +#. module: rpc_helper +#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form +msgid "RPC config" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config +msgid "Rpc Config" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit +msgid "Rpc Config Edit" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search +msgid "Smart Search" +msgstr "" + +#. module: rpc_helper +#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset +msgid "User Can See Changeset" +msgstr "" diff --git a/rpc_helper/models/__init__.py b/rpc_helper/models/__init__.py new file mode 100644 index 00000000000..413bb238014 --- /dev/null +++ b/rpc_helper/models/__init__.py @@ -0,0 +1 @@ +from . import ir_model diff --git a/rpc_helper/models/ir_model.py b/rpc_helper/models/ir_model.py new file mode 100644 index 00000000000..d430367fdb5 --- /dev/null +++ b/rpc_helper/models/ir_model.py @@ -0,0 +1,44 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import json + +from odoo import api, fields, models, tools + +from odoo.addons.base_sparse_field.models.fields import Serialized + + +class IrModel(models.Model): + _inherit = "ir.model" + + rpc_config = Serialized(compute="_compute_rpc_config", default={}) + # Allow editing via UI + rpc_config_edit = fields.Text( + help="Configure RPC config via JSON. " + "Value must be a list of methods to disable " + "wrapped by a dict with key `disable`. " + "Eg: {'disable': ['search', 'do_this']}" + "To disable all methods, use `{'disable: ['all']}`", + ) + + @api.depends("rpc_config_edit") + def _compute_rpc_config(self): + for rec in self: + rec.rpc_config = rec._load_rpc_config() + + @api.onchange("rpc_config_edit") + def _onchange_rpc_config_edit(self): + for rec in self: + # Make sure options_edit is always readable + rec.rpc_config_edit = json.dumps( + rec.rpc_config or {}, indent=4, sort_keys=True + ) + + def _load_rpc_config(self): + return json.loads(self.rpc_config_edit or "{}") + + @tools.ormcache("model") + def _get_rpc_config(self, model): + rec = self._get(model) + return rec.rpc_config or {} diff --git a/rpc_helper/patch.py b/rpc_helper/patch.py new file mode 100644 index 00000000000..82710068fa2 --- /dev/null +++ b/rpc_helper/patch.py @@ -0,0 +1,30 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import odoo +from odoo.exceptions import UserError +from odoo.tools.translate import _ + + +def protected__execute_cr(cr, uid, obj, method, *args, **kw): + # Same as original func in odoo.service.model.execute_cr + odoo.api.Environment.reset() # clean cache etc if we retry the same transaction + recs = odoo.api.Environment(cr, uid, {}).get(obj) + if recs is None: + raise UserError(_("Object %s doesn't exist") % obj) + # custom code starts here + if not _rpc_allowed(recs, method): + raise UserError(_("RPC call on %s is not allowed") % obj) + return protected__execute_cr._orig__execute_cr(cr, uid, obj, method, *args, **kw) + + +def _rpc_allowed(recordset, method): + config = getattr(recordset, "_disable_rpc", None) + if config is None: + config = ( + recordset.env["ir.model"]._get_rpc_config(recordset._name).get("disable") + ) + if config is None: + return True + return "all" not in config and method not in config diff --git a/rpc_helper/readme/CONFIGURE.rst b/rpc_helper/readme/CONFIGURE.rst new file mode 100644 index 00000000000..84347b529f3 --- /dev/null +++ b/rpc_helper/readme/CONFIGURE.rst @@ -0,0 +1,23 @@ +Enable debug mode and go to "Technical -> Database Structure -> Models". + +Open the model that you like to configure and go to the tab "RPC config". + +There you see a text field which supports JSON configuration. + +The configuration is the same you can pass via decorator. +The only difference is that you have to wrap values in a dictionary +like `{"disable": [...values...]}`. + +To disable all calls:: + + { + "disable": ["all"], + } + +To disable only some methods:: + + { + "disable": ["create", "write", "another_method"], + } + +NOTE: on the resulting JSON will be automatically formatted on save for better readability. diff --git a/rpc_helper/readme/CONTRIBUTORS.rst b/rpc_helper/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..37423d21eef --- /dev/null +++ b/rpc_helper/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Simone Orsi +* Guillem Casassas diff --git a/rpc_helper/readme/DESCRIPTION.rst b/rpc_helper/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..e5078d95216 --- /dev/null +++ b/rpc_helper/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Provide helpers to authorize RPC calls. diff --git a/rpc_helper/readme/USAGE.rst b/rpc_helper/readme/USAGE.rst new file mode 100644 index 00000000000..ff675247629 --- /dev/null +++ b/rpc_helper/readme/USAGE.rst @@ -0,0 +1,24 @@ +Via code +~~~~~~~~ + +Decorate an Odoo model class like this:: + + from odoo.addons.rpc_helper.decorator import disable_rpc + + @disable_rpc() + class AverageModel(models.Model): + _inherit = "avg.model" + +This will disable ALL calls. + +To selectively disable only some methods:: + + @disable_rpc("create", "write", "any_method") + class AverageModel(models.Model): + _inherit = "avg.model" + + +Via `ir.model` configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See "Configuration" section. diff --git a/rpc_helper/rpc_test_example.py b/rpc_helper/rpc_test_example.py new file mode 100644 index 00000000000..d48b9a73da3 --- /dev/null +++ b/rpc_helper/rpc_test_example.py @@ -0,0 +1,15 @@ +"""Basic example script you can use to test your own models for real. +""" +from xmlrpc import client + +HOST = "127.0.0.1" +PORT = 8069 +DB_NAME = "odoodb" + +url = "http://%s:%d/xmlrpc/2/" % (HOST, PORT) +xmlrpc_common = client.ServerProxy(url + "common") +xmlrpc_db = client.ServerProxy(url + "db") +xmlrpc_object = client.ServerProxy(url + "object") + +uid = xmlrpc_common.login(DB_NAME, "admin", "admin") +res = xmlrpc_object.execute(DB_NAME, uid, "admin", "res.partner", "search", []) diff --git a/rpc_helper/static/description/icon.png b/rpc_helper/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/rpc_helper/static/description/icon.png differ diff --git a/rpc_helper/static/description/index.html b/rpc_helper/static/description/index.html new file mode 100644 index 00000000000..bbf8f631910 --- /dev/null +++ b/rpc_helper/static/description/index.html @@ -0,0 +1,476 @@ + + + + + + +Disable RPC + + + +
+

Disable RPC

+ + +

Beta License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

+

Provide helpers to authorize RPC calls.

+

Table of contents

+ +
+

Configuration

+

Enable debug mode and go to “Technical -> Database Structure -> Models”.

+

Open the model that you like to configure and go to the tab “RPC config”.

+

There you see a text field which supports JSON configuration.

+

The configuration is the same you can pass via decorator. +The only difference is that you have to wrap values in a dictionary +like {“disable”: […values…]}.

+

To disable all calls:

+
+{
+    "disable": ["all"],
+}
+
+

To disable only some methods:

+
+{
+    "disable": ["create", "write", "another_method"],
+}
+
+

NOTE: on the resulting JSON will be automatically formatted on save for better readability.

+
+
+

Usage

+
+

Via code

+

Decorate an Odoo model class like this:

+
+from odoo.addons.rpc_helper.decorator import disable_rpc
+
+@disable_rpc()
+class AverageModel(models.Model):
+    _inherit = "avg.model"
+
+

This will disable ALL calls.

+

To selectively disable only some methods:

+
+@disable_rpc("create", "write", "any_method")
+class AverageModel(models.Model):
+    _inherit = "avg.model"
+
+
+
+

Via ir.model configuration

+

See “Configuration” section.

+
+
+
+

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

+
    +
  • Camptocamp
  • +
+
+ +
+

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:

+

simahawk

+

This module is part of the OCA/server-tools project on GitHub.

+

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

+
+
+
+ + diff --git a/rpc_helper/tests/__init__.py b/rpc_helper/tests/__init__.py new file mode 100644 index 00000000000..c60563b6ab5 --- /dev/null +++ b/rpc_helper/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_xmlrpc +from . import test_decorator diff --git a/rpc_helper/tests/test_decorator.py b/rpc_helper/tests/test_decorator.py new file mode 100644 index 00000000000..1ee3ee54f68 --- /dev/null +++ b/rpc_helper/tests/test_decorator.py @@ -0,0 +1,34 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +import unittest + +from ..decorator import disable_rpc + + +@disable_rpc() +class All: + pass + + +@disable_rpc("create") +class One: + pass + + +@disable_rpc("create", "write") +class Multi: + pass + + +class TestDecorator(unittest.TestCase): + def test_all(self): + self.assertEqual(All._disable_rpc, ("all",)) + + def test_one(self): + self.assertEqual(One._disable_rpc, ("create",)) + + def test_multi(self): + self.assertEqual(Multi._disable_rpc, ("create", "write")) diff --git a/rpc_helper/tests/test_xmlrpc.py b/rpc_helper/tests/test_xmlrpc.py new file mode 100644 index 00000000000..ecda9decfe8 --- /dev/null +++ b/rpc_helper/tests/test_xmlrpc.py @@ -0,0 +1,75 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +import json +import xmlrpc + +from odoo.tests import common + + +@common.tagged("post_install", "-at_install") +class TestXMLRPC(common.HttpCase): + def setUp(self): + super().setUp() + self.admin_uid = self.env.ref("base.user_admin").id + + def _set_disable(self, val): + type(self.env["res.partner"])._disable_rpc = val + + def _set_disable_on_model(self, val): + self.env["ir.model"]._get("res.partner").rpc_config_edit = json.dumps( + {"disable": val} + ) + + def tearDown(self): + klass = type(self.env["res.partner"]) + if hasattr(klass, "_disable_rpc"): + delattr(klass, "_disable_rpc") + super().tearDown() + + def _rpc_call(self, method, vals=None): + o = self.xmlrpc_object + db_name = common.get_db_name() + return o.execute( + db_name, self.admin_uid, "admin", "res.partner", method, vals or [] + ) + + def test_xmlrpc_search_normal(self): + res = self._rpc_call("search") + self.assertTrue(isinstance(res, list)) + + def test_xmlrpc_all_blocked(self): + self._set_disable(("all",)) + msg = "RPC call on res.partner is not allowed" + with self.assertRaisesRegex(xmlrpc.client.Fault, msg): + self._rpc_call("search") + + with self.assertRaisesRegex(xmlrpc.client.Fault, msg): + self._rpc_call("create", vals=[{"name": "Foo"}]) + + def test_xmlrpc_can_search_create_blocked(self): + self._set_disable(("create",)) + self._rpc_call("search") + + msg = "RPC call on res.partner is not allowed" + with self.assertRaisesRegex(xmlrpc.client.Fault, msg): + self._rpc_call("create", vals=[{"name": "Foo"}]) + + def test_xmlrpc_all_blocked__ir_model(self): + self._set_disable_on_model(("all",)) + msg = "RPC call on res.partner is not allowed" + with self.assertRaisesRegex(xmlrpc.client.Fault, msg): + self._rpc_call("search") + + with self.assertRaisesRegex(xmlrpc.client.Fault, msg): + self._rpc_call("create", vals=[{"name": "Foo"}]) + + def test_xmlrpc_can_search_create_blocked__ir_model(self): + self._set_disable_on_model(("create",)) + self._rpc_call("search") + + msg = "RPC call on res.partner is not allowed" + with self.assertRaisesRegex(xmlrpc.client.Fault, msg): + self._rpc_call("create", vals=[{"name": "Foo"}]) diff --git a/rpc_helper/views/ir_model_views.xml b/rpc_helper/views/ir_model_views.xml new file mode 100644 index 00000000000..0e1311f3be4 --- /dev/null +++ b/rpc_helper/views/ir_model_views.xml @@ -0,0 +1,23 @@ + + + + + rpc_helper view_model_form + ir.model + + + + + + + + + + diff --git a/setup/rpc_helper/odoo/addons/rpc_helper b/setup/rpc_helper/odoo/addons/rpc_helper new file mode 120000 index 00000000000..f1248ff2485 --- /dev/null +++ b/setup/rpc_helper/odoo/addons/rpc_helper @@ -0,0 +1 @@ +../../../../rpc_helper \ No newline at end of file diff --git a/setup/rpc_helper/setup.py b/setup/rpc_helper/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/rpc_helper/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)