diff --git a/server_environment_data_encryption/README.rst b/server_environment_data_encryption/README.rst new file mode 100644 index 0000000..4a3f962 --- /dev/null +++ b/server_environment_data_encryption/README.rst @@ -0,0 +1,112 @@ +================================== +Server Environment Data Encryption +================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |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%2Fserver--env-lightgray.png?logo=github + :target: https://github.com/OCA/server-env/tree/14.0/server_environment_data_encryption + :alt: OCA/server-env +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-env-14-0/server-env-14-0-server_environment_data_encryption + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/254/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module changes a little the behavior of server_environment modules. +When Odoo does not find the value of the field in the configuration file, +it will fallback on a Odoo encrypted field instead. +Also it allows you +to configure the environment dependent fields for all your environments +from the production server. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +In order to use this module properly, each environment should have their own encryption key +and the production environment should have the keys of all environments. + +Example : +Development environment :: + + [options] + running_env=dev + encryption_key_dev=XXX + +Pre-production environment :: + + [options] + running_env=preprod + encryption_key_preprod=YYY + +Production environment :: + + [options] + running_env=prod + encryption_key_dev=XXX + encryption_key_preprod=YYY + encryption_key_prod=ZZZ + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* Florian da Costa +* Sébastien Beau +* Benoît Guillot + +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. + +This module is part of the `OCA/server-env `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/server_environment_data_encryption/__init__.py b/server_environment_data_encryption/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/server_environment_data_encryption/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/server_environment_data_encryption/__manifest__.py b/server_environment_data_encryption/__manifest__.py new file mode 100644 index 0000000..97755ce --- /dev/null +++ b/server_environment_data_encryption/__manifest__.py @@ -0,0 +1,12 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Server Environment Data Encryption", + "version": "16.0.1.0.0", + "development_status": "Alpha", + "category": "Tools", + "website": "https://github.com/OCA/server-env", + "author": "Akretion, Odoo Community Association (OCA)", + "license": "AGPL-3", + "installable": True, + "depends": ["server_environment", "data_encryption"], +} diff --git a/server_environment_data_encryption/i18n/ca.po b/server_environment_data_encryption/i18n/ca.po new file mode 100644 index 0000000..a9a2ce9 --- /dev/null +++ b/server_environment_data_encryption/i18n/ca.po @@ -0,0 +1,60 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * server_environment_data_encryption +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ca\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" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "Define values for " +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model.fields,field_description:server_environment_data_encryption.field_server_env_mixin__display_name +msgid "Display Name" +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model.fields,field_description:server_environment_data_encryption.field_server_env_mixin__id +msgid "ID" +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model.fields,field_description:server_environment_data_encryption.field_server_env_mixin____last_update +msgid "Last Modified on" +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model,name:server_environment_data_encryption.model_server_env_mixin +msgid "Mixin to add server environment in existing models" +msgstr "" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "Modify values for {} environment" +msgstr "" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "The encryption key for current environement is not defined" +msgstr "" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "" +"you need to define the running_env entry in your odoo configuration file" +msgstr "" diff --git a/server_environment_data_encryption/i18n/server_environment_data_encryption.pot b/server_environment_data_encryption/i18n/server_environment_data_encryption.pot new file mode 100644 index 0000000..b53f2f0 --- /dev/null +++ b/server_environment_data_encryption/i18n/server_environment_data_encryption.pot @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * server_environment_data_encryption +# +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: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "Define values for " +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model.fields,field_description:server_environment_data_encryption.field_server_env_mixin__display_name +msgid "Display Name" +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model.fields,field_description:server_environment_data_encryption.field_server_env_mixin__id +msgid "ID" +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model.fields,field_description:server_environment_data_encryption.field_server_env_mixin____last_update +msgid "Last Modified on" +msgstr "" + +#. module: server_environment_data_encryption +#: model:ir.model,name:server_environment_data_encryption.model_server_env_mixin +msgid "Mixin to add server environment in existing models" +msgstr "" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "Modify values for {} environment" +msgstr "" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "The encryption key for current environement is not defined" +msgstr "" + +#. module: server_environment_data_encryption +#: code:addons/server_environment_data_encryption/models/server_env_mixin.py:0 +#, python-format +msgid "" +"you need to define the running_env entry in your odoo configuration file" +msgstr "" diff --git a/server_environment_data_encryption/models/__init__.py b/server_environment_data_encryption/models/__init__.py new file mode 100644 index 0000000..6bd869a --- /dev/null +++ b/server_environment_data_encryption/models/__init__.py @@ -0,0 +1 @@ +from . import server_env_mixin diff --git a/server_environment_data_encryption/models/server_env_mixin.py b/server_environment_data_encryption/models/server_env_mixin.py new file mode 100644 index 0000000..7cde5ff --- /dev/null +++ b/server_environment_data_encryption/models/server_env_mixin.py @@ -0,0 +1,201 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import json +import logging + +from lxml import etree + +from odoo import _, api, models +from odoo.exceptions import ValidationError +from odoo.tools.config import config + +_logger = logging.getLogger(__name__) + + +class ServerEnvMixin(models.AbstractModel): + _inherit = "server.env.mixin" + + def _current_env_encrypted_key_exists(self): + env = self.env["encrypted.data"]._retrieve_env() + key_name = "encryption_key_%s" % env + key_str = config.get(key_name) + key_exists = key_str and True or False + if not key_exists: + logging.warning( + "The minimal configuration is missing. You need at least to add an " + "encryption key for the current environment : %s. While the " + "configuration is missing, the module has no effect", + env, + ) + return key_exists + + def _compute_server_env_from_default(self, field_name, options): + """First return database encrypted value then default value""" + # in case of bad configuration (no encryption key for current env) the module + # is useless, we do fallback directly on serven_environement behavior + if not self._current_env_encrypted_key_exists(): + return super()._compute_server_env_from_default(field_name, options) + encrypted_data_name = "{},{}".format(self._name, self.id) + env = self.env.context.get("environment", None) + + vals = ( + self.env["encrypted.data"] + .sudo() + ._encrypted_read_json(encrypted_data_name, env=env) + ) + if vals.get(field_name): + self[field_name] = vals[field_name] + else: + return super()._compute_server_env_from_default(field_name, options) + + def _inverse_server_env(self, field_name): + """ + When this module is installed, we store values into encrypted data + env instead of a default field in database (not env dependent). + """ + # in case of bad configuration (no encryption key for current env) the module + # is useless, we do fallback directly on serven_environement behavior + if not self._current_env_encrypted_key_exists(): + return super()._inverse_server_env(field_name) + is_editable_field = self._server_env_is_editable_fieldname(field_name) + encrypted_data_obj = self.env["encrypted.data"].sudo() + env = self.env.context.get("environment", None) + for record in self: + if record[is_editable_field]: + encrypted_data_name = "{},{}".format(record._name, record.id) + values = encrypted_data_obj._encrypted_read_json( + encrypted_data_name, env=env + ) + new_val = {field_name: record[field_name]} + values.update(new_val) + encrypted_data_obj._encrypted_store_json( + encrypted_data_name, values, env=env + ) + + def action_change_env_data_encrypted_fields(self): + action_id = self.env.context.get("params", {}).get("action") + if not action_id: + # We don't know which action we are using... take default one + action = self.get_formview_action() + else: + action = ( + self.env["ir.actions.act_window"].browse(action_id).sudo().read()[0] + ) + action["view_mode"] = "form" + action["res_id"] = self.id + views_form = [] + for view_id, view_type in action.get("views", []): + if view_type == "form": + views_form.append((view_id, view_type)) + action["views"] = views_form + return action + + def _get_extra_environment_info_div(self, current_env, extra_envs): + # if the module configuration is missing (no current env encryption key) + # display a warning instead as the module has no effect. + if not self._current_env_encrypted_key_exists(): + button_div = "
" + warning_string = _( + "The encryption key for current environement is not defined" + ) + elem = etree.fromstring( + """ +
+
+ {} +
+
+ """.format( + "alert-danger", warning_string + ) + ) + return elem + + # TODO we could use a qweb template here + button_div = "
" + button_string = _("Define values for ") + for environment in extra_envs: + button = """ +
" + alert_string = _("Modify values for {} environment").format(current_env) + alert_type = ( + current_env == config.get("running_env") and "alert-info" or "alert-warning" + ) + elem = etree.fromstring( + """ +
+
+ {} +
+ {} +
+ """.format( + alert_type, alert_string, button_div + ) + ) + return elem + + def _set_readonly_form_view(self, doc): + for field in doc.iter("field"): + env_fields = self._server_env_fields.keys() + field_name = field.get("name") + if field_name in env_fields: + continue + field.set("readonly", "1") + field.set("modifiers", json.dumps({"readonly": True})) + + def _update_form_view_from_env(self, arch, view_type): + if view_type != "form": + return arch + current_env = self.env.context.get("environment") or config.get("running_env") + # Important to keep this list sorted. It makes sure the button to + # switch environment will always be in the same order. (more user + # friendly) and the test would fail without it as the order could + # change randomly and the view would then also change randomly + other_environments = sorted( + [ + key[15:] + for key, val in config.options.items() + if key.startswith("encryption_key_") and val and key[15:] != current_env + ] + ) + + if not current_env: + raise ValidationError( + _( + "you need to define the running_env entry in your odoo " + "configuration file" + ) + ) + node = arch.xpath("//sheet") + if node: + node = node[0] + elem = self._get_extra_environment_info_div(current_env, other_environments) + node.insert(0, elem) + + if current_env != config.get("running_env"): + self._set_readonly_form_view(arch) + else: + _logger.error("Missing sheet for form view on object {}".format(self._name)) + return arch + + @api.model + def _get_view(self, view_id=None, view_type="form", **options): + arch, view = super()._get_view(view_id=view_id, view_type=view_type, **options) + arch = self._update_form_view_from_env(arch, view_type) + return arch, view + + def _get_view_cache_key(self, view_id=None, view_type="form", **options): + res = super()._get_view_cache_key( + view_id=view_id, view_type=view_type, **options + ) + res += (self.env.context.get("environment", False),) + return res diff --git a/server_environment_data_encryption/readme/CONFIGURE.rst b/server_environment_data_encryption/readme/CONFIGURE.rst new file mode 100644 index 0000000..37875fa --- /dev/null +++ b/server_environment_data_encryption/readme/CONFIGURE.rst @@ -0,0 +1,23 @@ +In order to use this module properly, each environment should have their own encryption key +and the production environment should have the keys of all environments. + +Example : +Development environment :: + + [options] + running_env=dev + encryption_key_dev=XXX + +Pre-production environment :: + + [options] + running_env=preprod + encryption_key_preprod=YYY + +Production environment :: + + [options] + running_env=prod + encryption_key_dev=XXX + encryption_key_preprod=YYY + encryption_key_prod=ZZZ diff --git a/server_environment_data_encryption/readme/CONTRIBUTORS.rst b/server_environment_data_encryption/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..86340ec --- /dev/null +++ b/server_environment_data_encryption/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Florian da Costa +* Sébastien Beau +* Benoît Guillot diff --git a/server_environment_data_encryption/readme/DESCRIPTION.rst b/server_environment_data_encryption/readme/DESCRIPTION.rst new file mode 100644 index 0000000..ebf4147 --- /dev/null +++ b/server_environment_data_encryption/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module changes a little the behavior of server_environment modules. +When Odoo does not find the value of the field in the configuration file, +it will fallback on a Odoo encrypted field instead. +Also it allows you +to configure the environment dependent fields for all your environments +from the production server. diff --git a/server_environment_data_encryption/static/description/icon.png b/server_environment_data_encryption/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/server_environment_data_encryption/static/description/icon.png differ diff --git a/server_environment_data_encryption/static/description/index.html b/server_environment_data_encryption/static/description/index.html new file mode 100644 index 0000000..bd00ca0 --- /dev/null +++ b/server_environment_data_encryption/static/description/index.html @@ -0,0 +1,459 @@ + + + + + + +Server Environment Data Encryption + + + +
+

Server Environment Data Encryption

+ + +

Alpha License: AGPL-3 OCA/server-env Translate me on Weblate Try me on Runbot

+

This module changes a little the behavior of server_environment modules. +When Odoo does not find the value of the field in the configuration file, +it will fallback on a Odoo encrypted field instead. +Also it allows you +to configure the environment dependent fields for all your environments +from the production server.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Configuration

+

In order to use this module properly, each environment should have their own encryption key +and the production environment should have the keys of all environments.

+

Example : +Development environment

+
+[options]
+running_env=dev
+encryption_key_dev=XXX
+
+

Pre-production environment

+
+[options]
+running_env=preprod
+encryption_key_preprod=YYY
+
+

Production environment

+
+[options]
+running_env=prod
+encryption_key_dev=XXX
+encryption_key_preprod=YYY
+encryption_key_prod=ZZZ
+
+
+
+

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 smashing 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.

+

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

+

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

+
+
+
+ + diff --git a/server_environment_data_encryption/tests/__init__.py b/server_environment_data_encryption/tests/__init__.py new file mode 100644 index 0000000..83ac8b8 --- /dev/null +++ b/server_environment_data_encryption/tests/__init__.py @@ -0,0 +1 @@ +from . import test_server_environment_data_encrypt diff --git a/server_environment_data_encryption/tests/fixtures/base.xml b/server_environment_data_encryption/tests/fixtures/base.xml new file mode 100644 index 0000000..11f2eb7 --- /dev/null +++ b/server_environment_data_encryption/tests/fixtures/base.xml @@ -0,0 +1,22 @@ +
+
+
+ + + + + + + +
diff --git a/server_environment_data_encryption/tests/fixtures/res1.xml b/server_environment_data_encryption/tests/fixtures/res1.xml new file mode 100644 index 0000000..0248d10 --- /dev/null +++ b/server_environment_data_encryption/tests/fixtures/res1.xml @@ -0,0 +1,43 @@ +
+
+
+ +
+
+ Modify values for test environment +
+
+
+
+ + + + +
+
diff --git a/server_environment_data_encryption/tests/fixtures/res2.xml b/server_environment_data_encryption/tests/fixtures/res2.xml new file mode 100644 index 0000000..d49648f --- /dev/null +++ b/server_environment_data_encryption/tests/fixtures/res2.xml @@ -0,0 +1,44 @@ +
+
+
+ +
+
+ Modify values for prod environment +
+
+
+
+ + + + +
+
diff --git a/server_environment_data_encryption/tests/test_server_environment_data_encrypt.py b/server_environment_data_encryption/tests/test_server_environment_data_encrypt.py new file mode 100644 index 0000000..6a10549 --- /dev/null +++ b/server_environment_data_encryption/tests/test_server_environment_data_encrypt.py @@ -0,0 +1,47 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from pathlib import Path + +from lxml import etree + +from odoo.addons.data_encryption.tests.common import CommonDataEncrypted + + +class TestServerEnvDataEncrypted(CommonDataEncrypted): + def test_dynamic_view_current_env(self): + self.maxDiff = None + self.set_new_key_env("prod") + self.set_new_key_env("preprod") + mixin_obj = self.env["server.env.mixin"] + base_path = Path(__file__).parent / "fixtures" / "base.xml" + xml_str = base_path.read_text() + xml = etree.XML(xml_str) + res_xml = mixin_obj._update_form_view_from_env(xml, "form") + expected_xml_path = Path(__file__).parent / "fixtures" / "res1.xml" + expected_xml = expected_xml_path.read_text() + # convert both to xml with parser removing space then convert to string to + # compare + parser = etree.XMLParser(remove_blank_text=True) + res_xml_str = etree.tostring(etree.XML(etree.tostring(res_xml), parser=parser)) + expected_xml_str = etree.tostring(etree.XML(expected_xml, parser=parser)) + self.assertEqual(res_xml_str, expected_xml_str) + + def test_dynamic_view_other_env(self): + self.maxDiff = None + self.set_new_key_env("prod") + self.set_new_key_env("preprod") + mixin_obj = self.env["server.env.mixin"] + base_path = Path(__file__).parent / "fixtures" / "base.xml" + xml_str = base_path.read_text() + xml = etree.XML(xml_str) + res_xml = mixin_obj.with_context(environment="prod")._update_form_view_from_env( + xml, "form" + ) + expected_xml_path = Path(__file__).parent / "fixtures" / "res2.xml" + expected_xml = expected_xml_path.read_text() + # convert both to xml with parser removing space then convert to string to + # compare + parser = etree.XMLParser(remove_blank_text=True) + res_xml_str = etree.tostring(etree.XML(etree.tostring(res_xml), parser=parser)) + expected_xml_str = etree.tostring(etree.XML(expected_xml, parser=parser)) + self.assertEqual(res_xml_str, expected_xml_str) diff --git a/setup/server_environment_data_encryption/odoo/addons/server_environment_data_encryption b/setup/server_environment_data_encryption/odoo/addons/server_environment_data_encryption new file mode 120000 index 0000000..8052eed --- /dev/null +++ b/setup/server_environment_data_encryption/odoo/addons/server_environment_data_encryption @@ -0,0 +1 @@ +../../../../server_environment_data_encryption \ No newline at end of file diff --git a/setup/server_environment_data_encryption/setup.py b/setup/server_environment_data_encryption/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/server_environment_data_encryption/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)