diff --git a/data_encryption/README.rst b/data_encryption/README.rst new file mode 100644 index 0000000..6356515 --- /dev/null +++ b/data_encryption/README.rst @@ -0,0 +1,115 @@ +=============== +Encryption data +=============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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/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-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 allows to encrypt and decrypt data. This module is not usable +by itself, it is a low level module which should work as a base for others. +An example is the module server_environment_data_encryption + +.. 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 +============= + +To configure this module, you need to edit the main configuration file +of your instance, and add a directive called ``running_env``. Commonly +used values are 'dev', 'test', 'production':: + + [options] + running_env=dev + + +You also need to set the encryption key(s). The main idea is to have different +encryption keys for your different environment, to avoid the possibility to retrieve +crucial information from the production environment in a developement environment, for instance. +So, if your running environment is 'dev':: + + [options] + encryption_key_dev=fyeMIx9XVPBBky5XZeLDxVc9dFKy7Uzas3AoyMarHPA= + +In the configuration file of your production environment, you may want to configure +all your other environments encryption key. This way, from production you can encrypt and decrypt +data for all environments. + +You can generate keys with python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key())'. + +Known issues / Roadmap +====================== + +For now the encryption is dependent on the environment. It has been designed +to store the same kind of data with different values depending on the environement +(dev, preprod, prod...). +An improvement could be to split this in 2 modules. But the environment stuff +is not a big constraint. + +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 +~~~~~~~~~~~~ + +* Raphaël Reverdy +* Florian da Costa + +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/data_encryption/__init__.py b/data_encryption/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/data_encryption/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/data_encryption/__manifest__.py b/data_encryption/__manifest__.py new file mode 100644 index 0000000..fac10cb --- /dev/null +++ b/data_encryption/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright <2019> Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Encryption data", + "summary": "Store accounts and credentials encrypted by environment", + "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", + "application": False, + "installable": True, + "external_dependencies": {"python": ["cryptography"]}, + "depends": ["base"], + "data": ["security/ir.model.access.csv"], +} diff --git a/data_encryption/i18n/ca.po b/data_encryption/i18n/ca.po new file mode 100644 index 0000000..266b4bb --- /dev/null +++ b/data_encryption/i18n/ca.po @@ -0,0 +1,127 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * 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: data_encryption +#: model:ir.model.fields,help:data_encryption.field_encrypted_data__environment +msgid "Concerned Odoo environment (prod, preprod...)" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__create_uid +msgid "Created by" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__create_date +msgid "Created on" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__display_name +msgid "Display Name" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__encrypted_data +msgid "Encrypted Data" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "Encrypted data can only be read with suspended security (sudo)" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__environment +msgid "Environment" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__id +msgid "ID" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data____last_update +msgid "Last Modified on" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__write_date +msgid "Last Updated on" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__name +msgid "Name" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "No '%s' entry found in config file. Use a key similar to: %s" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "" +"No environment found, please check your running_env entry in your config " +"file." +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "" +"Password has been encrypted with a different key. Unless you can recover the" +" previous key, this password is unreadable." +msgstr "" + +#. module: data_encryption +#: model:ir.model,name:data_encryption.model_encrypted_data +msgid "Store any encrypted data by environment" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,help:data_encryption.field_encrypted_data__name +msgid "Technical name" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "The data you are trying to read are not in a json format" +msgstr "" + +#. module: data_encryption +#: model:ir.model.constraint,message:data_encryption.constraint_encrypted_data_name_environment_uniq +msgid "" +"You can not store multiple encrypted data for the same record and " +"environment" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "You can only encrypt data with suspended security (sudo)" +msgstr "" diff --git a/data_encryption/i18n/data_encryption.pot b/data_encryption/i18n/data_encryption.pot new file mode 100644 index 0000000..b330981 --- /dev/null +++ b/data_encryption/i18n/data_encryption.pot @@ -0,0 +1,126 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * 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: data_encryption +#: model:ir.model.fields,help:data_encryption.field_encrypted_data__environment +msgid "Concerned Odoo environment (prod, preprod...)" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__create_uid +msgid "Created by" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__create_date +msgid "Created on" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__display_name +msgid "Display Name" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__encrypted_data +msgid "Encrypted Data" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "Encrypted data can only be read with suspended security (sudo)" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__environment +msgid "Environment" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__id +msgid "ID" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data____last_update +msgid "Last Modified on" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__write_date +msgid "Last Updated on" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,field_description:data_encryption.field_encrypted_data__name +msgid "Name" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "No '%s' entry found in config file. Use a key similar to: %s" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "" +"No environment found, please check your running_env entry in your config " +"file." +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "" +"Password has been encrypted with a different key. Unless you can recover the" +" previous key, this password is unreadable." +msgstr "" + +#. module: data_encryption +#: model:ir.model,name:data_encryption.model_encrypted_data +msgid "Store any encrypted data by environment" +msgstr "" + +#. module: data_encryption +#: model:ir.model.fields,help:data_encryption.field_encrypted_data__name +msgid "Technical name" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "The data you are trying to read are not in a json format" +msgstr "" + +#. module: data_encryption +#: model:ir.model.constraint,message:data_encryption.constraint_encrypted_data_name_environment_uniq +msgid "" +"You can not store multiple encrypted data for the same record and " +"environment" +msgstr "" + +#. module: data_encryption +#: code:addons/data_encryption/models/encrypted_data.py:0 +#, python-format +msgid "You can only encrypt data with suspended security (sudo)" +msgstr "" diff --git a/data_encryption/models/__init__.py b/data_encryption/models/__init__.py new file mode 100644 index 0000000..da8f5b3 --- /dev/null +++ b/data_encryption/models/__init__.py @@ -0,0 +1 @@ +from . import encrypted_data diff --git a/data_encryption/models/encrypted_data.py b/data_encryption/models/encrypted_data.py new file mode 100644 index 0000000..2e95265 --- /dev/null +++ b/data_encryption/models/encrypted_data.py @@ -0,0 +1,148 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import json +import logging + +from odoo import api, fields, models +from odoo.exceptions import AccessError, ValidationError +from odoo.tools import ormcache +from odoo.tools.config import config +from odoo.tools.translate import _ + +_logger = logging.getLogger(__name__) + +try: + from cryptography.fernet import Fernet, InvalidToken +except ImportError as err: # pragma: no cover + _logger.debug(err) + + +class EncryptedData(models.Model): + """Model to store encrypted data by environment (prod, preprod...)""" + + _name = "encrypted.data" + _description = "Store any encrypted data by environment" + + name = fields.Char(required=True, readonly=True, index=True, help="Technical name") + environment = fields.Char( + required=True, + index=True, + help="Concerned Odoo environment (prod, preprod...)", + ) + encrypted_data = fields.Binary(attachment=False) + + _sql_constraints = [ + ( + "name_environment_uniq", + "unique (name, environment)", + "You can not store multiple encrypted data for the same record and \ + environment", + ) + ] + + def _decrypt_data(self, env): + self.ensure_one() + cipher = self._get_cipher(env) + try: + return cipher.decrypt(self.encrypted_data).decode() + except InvalidToken as exc: + raise ValidationError( + _( + "Password has been encrypted with a different " + "key. Unless you can recover the previous key, " + "this password is unreadable." + ) + ) from exc + + @api.model + @ormcache("self._uid", "name", "env") + def _encrypted_get(self, name, env=None): + if self.env.context.get("bin_size"): + self = self.with_context(bin_size=False) + if not self.env.su: + raise AccessError( + _("Encrypted data can only be read with suspended security (sudo)") + ) + if not env: + env = self._retrieve_env() + encrypted_rec = self.search([("name", "=", name), ("environment", "=", env)]) + if not encrypted_rec: + return None + return encrypted_rec._decrypt_data(env) + + @api.model + @ormcache("self._uid", "name", "env") + def _encrypted_read_json(self, name, env=None): + data = self._encrypted_get(name, env=env) + if not data: + return {} + try: + return json.loads(data) + except (ValueError, TypeError) as exc: + raise ValidationError( + _("The data you are trying to read are not in a json format") + ) from exc + + @staticmethod + def _retrieve_env(): + """Return the current environment + Raise if none is found + """ + current = config.get("running_env", False) + if not current: + raise ValidationError( + _( + "No environment found, please check your running_env " + "entry in your config file." + ) + ) + return current + + @classmethod + def _get_cipher(cls, env): + """Return a cipher using the key of environment. + force_env = name of the env key. + Useful for encoding against one precise env + """ + key_name = "encryption_key_%s" % env + key_str = config.get(key_name) + if not key_str: + raise ValidationError( + _( + "No '%(key_name)s' entry found in config file. " + "Use a key similar to: %(key)s" + ) + % {"key_name": key_name, "key": Fernet.generate_key()} + ) + # key should be in bytes format + key = key_str.encode() + return Fernet(key) + + @api.model + def _encrypt_data(self, data, env): + cipher = self._get_cipher(env) + if not isinstance(data, bytes): + data = data.encode() + return cipher.encrypt(data or "") + + @api.model + def _encrypted_store(self, name, data, env=None): + if not self.env.su: + raise AccessError( + _("You can only encrypt data with suspended security (sudo)") + ) + if not env: + env = self._retrieve_env() + encrypted_data = self._encrypt_data(data, env) + existing_data = self.search([("name", "=", name), ("environment", "=", env)]) + if existing_data: + existing_data.write({"encrypted_data": encrypted_data}) + else: + self.create( + {"name": name, "environment": env, "encrypted_data": encrypted_data} + ) + self._encrypted_get.clear_cache(self) + self._encrypted_read_json.clear_cache(self) + + @api.model + def _encrypted_store_json(self, name, json_data, env=None): + return self._encrypted_store(name, json.dumps(json_data), env=env) diff --git a/data_encryption/readme/CONFIGURE.rst b/data_encryption/readme/CONFIGURE.rst new file mode 100644 index 0000000..69b4773 --- /dev/null +++ b/data_encryption/readme/CONFIGURE.rst @@ -0,0 +1,21 @@ +To configure this module, you need to edit the main configuration file +of your instance, and add a directive called ``running_env``. Commonly +used values are 'dev', 'test', 'production':: + + [options] + running_env=dev + + +You also need to set the encryption key(s). The main idea is to have different +encryption keys for your different environment, to avoid the possibility to retrieve +crucial information from the production environment in a developement environment, for instance. +So, if your running environment is 'dev':: + + [options] + encryption_key_dev=fyeMIx9XVPBBky5XZeLDxVc9dFKy7Uzas3AoyMarHPA= + +In the configuration file of your production environment, you may want to configure +all your other environments encryption key. This way, from production you can encrypt and decrypt +data for all environments. + +You can generate keys with python -c 'from cryptography.fernet import Fernet; print Fernet.generate_key()'. diff --git a/data_encryption/readme/CONTRIBUTORS.rst b/data_encryption/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..3017ba2 --- /dev/null +++ b/data_encryption/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Raphaël Reverdy +* Florian da Costa diff --git a/data_encryption/readme/DESCRIPTION.rst b/data_encryption/readme/DESCRIPTION.rst new file mode 100644 index 0000000..9c4dc15 --- /dev/null +++ b/data_encryption/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module allows to encrypt and decrypt data. This module is not usable +by itself, it is a low level module which should work as a base for others. +An example is the module server_environment_data_encryption diff --git a/data_encryption/readme/ROADMAP.rst b/data_encryption/readme/ROADMAP.rst new file mode 100644 index 0000000..71a6a12 --- /dev/null +++ b/data_encryption/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +For now the encryption is dependent on the environment. It has been designed +to store the same kind of data with different values depending on the environement +(dev, preprod, prod...). +An improvement could be to split this in 2 modules. But the environment stuff +is not a big constraint. diff --git a/data_encryption/security/ir.model.access.csv b/data_encryption/security/ir.model.access.csv new file mode 100644 index 0000000..ddbd5f7 --- /dev/null +++ b/data_encryption/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_encrypted_data,access_encrypted_data,model_encrypted_data,base.group_system,0,0,0,0 diff --git a/data_encryption/static/description/icon.png b/data_encryption/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/data_encryption/static/description/icon.png differ diff --git a/data_encryption/static/description/index.html b/data_encryption/static/description/index.html new file mode 100644 index 0000000..e35494d --- /dev/null +++ b/data_encryption/static/description/index.html @@ -0,0 +1,460 @@ + + + + + + +Encryption data + + + +
+

Encryption data

+ + +

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

+

This module allows to encrypt and decrypt data. This module is not usable +by itself, it is a low level module which should work as a base for others. +An example is the module server_environment_data_encryption

+
+

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

+

To configure this module, you need to edit the main configuration file +of your instance, and add a directive called running_env. Commonly +used values are ‘dev’, ‘test’, ‘production’:

+
+[options]
+running_env=dev
+
+

You also need to set the encryption key(s). The main idea is to have different +encryption keys for your different environment, to avoid the possibility to retrieve +crucial information from the production environment in a developement environment, for instance. +So, if your running environment is ‘dev’:

+
+[options]
+encryption_key_dev=fyeMIx9XVPBBky5XZeLDxVc9dFKy7Uzas3AoyMarHPA=
+
+

In the configuration file of your production environment, you may want to configure +all your other environments encryption key. This way, from production you can encrypt and decrypt +data for all environments.

+

You can generate keys with python -c ‘from cryptography.fernet import Fernet; print Fernet.generate_key()’.

+
+
+

Known issues / Roadmap

+

For now the encryption is dependent on the environment. It has been designed +to store the same kind of data with different values depending on the environement +(dev, preprod, prod…). +An improvement could be to split this in 2 modules. But the environment stuff +is not a big constraint.

+
+
+

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/data_encryption/tests/__init__.py b/data_encryption/tests/__init__.py new file mode 100644 index 0000000..d686b32 --- /dev/null +++ b/data_encryption/tests/__init__.py @@ -0,0 +1 @@ +from . import test_data_encrypt diff --git a/data_encryption/tests/common.py b/data_encryption/tests/common.py new file mode 100644 index 0000000..a57377d --- /dev/null +++ b/data_encryption/tests/common.py @@ -0,0 +1,36 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo.tests.common import TransactionCase +from odoo.tools.config import config + +_logger = logging.getLogger(__name__) + +try: + from cryptography.fernet import Fernet +except ImportError as err: # pragma: no cover + _logger.debug(err) + + +class CommonDataEncrypted(TransactionCase): + def setUp(self): + super().setUp() + + self.encrypted_data = self.env["encrypted.data"] + self.set_new_key_env("test") + self.old_running_env = config.get("running_env", "") + config["running_env"] = "test" + self.crypted_data_name = "test_model,1" + + def set_new_key_env(self, environment): + crypting_key = Fernet.generate_key() + # The key is encoded to bytes in the module, because in real life + # the key com from the config file and is not in a binary format. + # So we decode here to avoid having a special behavior because of + # the tests. + config["encryption_key_{}".format(environment)] = crypting_key.decode() + + def tearDown(self): + config["running_env"] = self.old_running_env + return super().tearDown() diff --git a/data_encryption/tests/test_data_encrypt.py b/data_encryption/tests/test_data_encrypt.py new file mode 100644 index 0000000..7f25053 --- /dev/null +++ b/data_encryption/tests/test_data_encrypt.py @@ -0,0 +1,92 @@ +# © 2016 Akretion Raphaël REVERDY +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo.exceptions import AccessError, ValidationError +from odoo.tools.config import config + +from .common import CommonDataEncrypted + +_logger = logging.getLogger(__name__) + +try: + from cryptography.fernet import Fernet +except ImportError as err: # pragma: no cover + _logger.debug(err) + + +class TestDataEncrypted(CommonDataEncrypted): + def test_store_data_no_superuser(self): + # only superuser can use this model + admin = self.env.ref("base.user_admin") + with self.assertRaises(AccessError): + self.encrypted_data.with_user(admin.id)._encrypted_store( + self.crypted_data_name, "My config" + ) + + def test_store_data_noenv_set(self): + config.pop("running_env", None) + with self.assertRaises(ValidationError): + self.encrypted_data.sudo()._encrypted_store( + self.crypted_data_name, "My config" + ) + + def test_store_data_nokey_set(self): + config.pop("encryption_key_test", None) + with self.assertRaises(ValidationError): + self.encrypted_data.sudo()._encrypted_store( + self.crypted_data_name, "My config" + ) + + def test_get_data_decrypted_and_cache(self): + self.encrypted_data.sudo()._encrypted_store("test_model,1", "My config") + data = self.encrypted_data.sudo()._encrypted_get(self.crypted_data_name) + self.assertEqual(data, "My config") + + # Test cache really depends on user (super user) else any user could + # access the data + admin = self.env.ref("base.user_admin") + with self.assertRaises(AccessError): + self.encrypted_data.with_user(admin)._encrypted_get(self.crypted_data_name) + + # Change value should invalidate cache + self.encrypted_data.sudo()._encrypted_store("test_model,1", "Other Config") + new_data = self.encrypted_data.sudo()._encrypted_get(self.crypted_data_name) + self.assertEqual(new_data, "Other Config") + + def test_get_data_wrong_key(self): + self.encrypted_data.sudo()._encrypted_store("test_model,1", "My config") + new_key = Fernet.generate_key() + config["encryption_key_test"] = new_key.decode() + with self.assertRaises(ValidationError): + self.encrypted_data.sudo()._encrypted_get(self.crypted_data_name) + + def test_get_empty_data(self): + empty_data = self.encrypted_data.sudo()._encrypted_get(self.crypted_data_name) + self.assertEqual(empty_data, None) + + def test_get_wrong_json(self): + self.encrypted_data.sudo()._encrypted_store(self.crypted_data_name, "config") + with self.assertRaises(ValidationError): + self.encrypted_data.sudo()._encrypted_read_json(self.crypted_data_name) + + def test_get_good_json(self): + self.encrypted_data.sudo()._encrypted_store_json( + self.crypted_data_name, {"key": "value"} + ) + data = self.encrypted_data.sudo()._encrypted_read_json(self.crypted_data_name) + self.assertEqual(data, {"key": "value"}) + + def test_get_empty_json(self): + data = self.encrypted_data.sudo()._encrypted_read_json(self.crypted_data_name) + self.assertEqual(data, {}) + + def test_get_data_with_bin_size_context(self): + self.encrypted_data.sudo()._encrypted_store(self.crypted_data_name, "test") + data = ( + self.encrypted_data.sudo() + .with_context(bin_size=True) + ._encrypted_get(self.crypted_data_name) + ) + self.assertEqual(data, "test") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..368c5c2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# generated from manifests external_dependencies +cryptography diff --git a/setup/data_encryption/odoo/addons/data_encryption b/setup/data_encryption/odoo/addons/data_encryption new file mode 120000 index 0000000..2aee058 --- /dev/null +++ b/setup/data_encryption/odoo/addons/data_encryption @@ -0,0 +1 @@ +../../../../data_encryption \ No newline at end of file diff --git a/setup/data_encryption/setup.py b/setup/data_encryption/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/data_encryption/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)