Merge PR #123 into 16.0

Signed-off-by rvalyi
This commit is contained in:
OCA-git-bot 2022-10-24 21:43:14 +00:00
commit c5b045854c
20 changed files with 1166 additions and 0 deletions

115
data_encryption/README.rst Normal file
View File

@ -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 <https://odoo-community.org/page/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 <https://github.com/OCA/server-env/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 <https://github.com/OCA/server-env/issues/new?body=module:%20data_encryption%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Akretion
Contributors
~~~~~~~~~~~~
* Raphaël Reverdy <raphael.reverdy@akretion.com>
* Florian da Costa <florian.dacosta@akretion.com>
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 <https://github.com/OCA/server-env/tree/14.0/data_encryption>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -0,0 +1 @@
from . import models

View File

@ -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"],
}

127
data_encryption/i18n/ca.po Normal file
View File

@ -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 ""

View File

@ -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 ""

View File

@ -0,0 +1 @@
from . import encrypted_data

View File

@ -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)

View File

@ -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()'.

View File

@ -0,0 +1,2 @@
* Raphaël Reverdy <raphael.reverdy@akretion.com>
* Florian da Costa <florian.dacosta@akretion.com>

View File

@ -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

View File

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

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_encrypted_data access_encrypted_data model_encrypted_data base.group_system 0 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,460 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Encryption data</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="encryption-data">
<h1 class="title">Encryption data</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/server-env/tree/14.0/data_encryption"><img alt="OCA/server-env" src="https://img.shields.io/badge/github-OCA%2Fserver--env-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/server-env-14-0/server-env-14-0-data_encryption"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/254/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>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</p>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">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.
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
<p>To configure this module, you need to edit the main configuration file
of your instance, and add a directive called <tt class="docutils literal">running_env</tt>. Commonly
used values are dev, test, production:</p>
<pre class="literal-block">
[options]
running_env=dev
</pre>
<p>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:</p>
<pre class="literal-block">
[options]
encryption_key_dev=fyeMIx9XVPBBky5XZeLDxVc9dFKy7Uzas3AoyMarHPA=
</pre>
<p>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.</p>
<p>You can generate keys with python -c from cryptography.fernet import Fernet; print Fernet.generate_key().</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
<p>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.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-env/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/server-env/issues/new?body=module:%20data_encryption%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Raphaël Reverdy &lt;<a class="reference external" href="mailto:raphael.reverdy&#64;akretion.com">raphael.reverdy&#64;akretion.com</a>&gt;</li>
<li>Florian da Costa &lt;<a class="reference external" href="mailto:florian.dacosta&#64;akretion.com">florian.dacosta&#64;akretion.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-env/tree/14.0/data_encryption">OCA/server-env</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1 @@
from . import test_data_encrypt

View File

@ -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()

View File

@ -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")

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
# generated from manifests external_dependencies
cryptography

View File

@ -0,0 +1 @@
../../../../data_encryption

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)