diff --git a/setup/vault_share/odoo/addons/vault_share b/setup/vault_share/odoo/addons/vault_share new file mode 120000 index 0000000000..d8d730a844 --- /dev/null +++ b/setup/vault_share/odoo/addons/vault_share @@ -0,0 +1 @@ +../../../../vault_share \ No newline at end of file diff --git a/setup/vault_share/setup.py b/setup/vault_share/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/vault_share/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/vault/static/description/icon.png b/vault/static/description/icon.png index a0c9035d9b..d79aa6f575 100644 Binary files a/vault/static/description/icon.png and b/vault/static/description/icon.png differ diff --git a/vault_share/README.rst b/vault_share/README.rst new file mode 100644 index 0000000000..da29dd4d6c --- /dev/null +++ b/vault_share/README.rst @@ -0,0 +1,86 @@ +============= +Vault - Share +============= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:9a16926a6329561017dac0fa81e331bcd50b83bc373281609831a25e42fb3e0c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github + :target: https://github.com/OCA/server-auth/tree/16.0/vault_share + :alt: OCA/server-auth +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-vault_share + :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-auth&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module implements possibilities to share specific secrets with external users. This bases on the vault implementation and the generated RSA key pair. + +Share +===== + +This allows an user to share a secret with external users. A share can be generated from a vault entry or directly created by an user. The secret is symmetrically encrypted by a key derived from a pin. To grant access the user has to transmit the link and pin with the external. If either the access counter reaches 0 or the share expires it will be deleted automatically. Due to the usage of a numeric pin and the browser side decryption a share is vulnerable to brute-force attacks and shouldn't be used as a permanent storage for secrets. For long time uses the user should create an account and a vault should be used. + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +* Secure the download of the encrypted file behind a challenge/response + +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 +~~~~~~~ + +* initOS GmbH + +Contributors +~~~~~~~~~~~~ + +* Florian Kantelberg + +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-auth `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/vault_share/__init__.py b/vault_share/__init__.py new file mode 100644 index 0000000000..05ae53c8cb --- /dev/null +++ b/vault_share/__init__.py @@ -0,0 +1,4 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import controllers, models diff --git a/vault_share/__manifest__.py b/vault_share/__manifest__.py new file mode 100644 index 0000000000..7e05ea658a --- /dev/null +++ b/vault_share/__manifest__.py @@ -0,0 +1,35 @@ +# © 2021-2024 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Vault - Share", + "summary": "Implementation of a mechanism to share secrets", + "license": "AGPL-3", + "version": "16.0.1.0.0", + "website": "https://github.com/OCA/server-auth", + "application": False, + "author": "initOS GmbH, Odoo Community Association (OCA)", + "category": "Vault", + "depends": ["vault"], + "data": [ + "data/ir_cron.xml", + "security/ir.model.access.csv", + "security/ir_rule.xml", + "views/menuitems.xml", + "views/res_config_settings_views.xml", + "views/templates.xml", + "views/vault_share_views.xml", + ], + "assets": { + "web.assets_backend": [ + "vault_share/static/src/common/**/*.js", + "vault_share/static/src/backend/**/*.js", + "vault_share/static/src/backend/**/*.scss", + "vault_share/static/src/backend/**/*.xml", + ], + "vault_share.assets_frontend": [ + "vault/static/src/common/*.js", + "vault_share/static/src/frontend/*.js", + ], + }, +} diff --git a/vault_share/controllers/__init__.py b/vault_share/controllers/__init__.py new file mode 100644 index 0000000000..aabfa83edd --- /dev/null +++ b/vault_share/controllers/__init__.py @@ -0,0 +1,4 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import main diff --git a/vault_share/controllers/main.py b/vault_share/controllers/main.py new file mode 100644 index 0000000000..52eb94d1ef --- /dev/null +++ b/vault_share/controllers/main.py @@ -0,0 +1,36 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import _, http +from odoo.http import request + +_logger = logging.getLogger(__name__) + + +class Controller(http.Controller): + @http.route("/vault/share/", type="http", auth="public") + def vault_share(self, token): + ctx = {"disable_footer": True, "token": token} + share = request.env["vault.share"].sudo() + secret = share.get(token, ip=request.httprequest.remote_addr) + if secret is None: + ctx["error"] = _("The secret expired") + return request.render("vault_share.share", ctx) + + if len(secret) != 1: + ctx["error"] = _("Invalid token") + return request.render("vault_share.share", ctx) + + ctx.update( + { + "encrypted": secret.secret, + "salt": secret.salt, + "iv": secret.iv, + "encrypted_file": secret.secret_file, + "filename": secret.filename, + "iterations": secret.iterations, + } + ) + return request.render("vault_share.share", ctx) diff --git a/vault_share/data/ir_cron.xml b/vault_share/data/ir_cron.xml new file mode 100644 index 0000000000..362e5c1905 --- /dev/null +++ b/vault_share/data/ir_cron.xml @@ -0,0 +1,13 @@ + + + + Clean outgoing share + + code + model.clean() + 1 + minutes + -1 + + + diff --git a/vault_share/i18n/es.po b/vault_share/i18n/es.po new file mode 100644 index 0000000000..a73a93b1cb --- /dev/null +++ b/vault_share/i18n/es.po @@ -0,0 +1,296 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * vault_share +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-06-06 07:44+0000\n" +"PO-Revision-Date: 2023-10-30 21:37+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__accesses +msgid "Access counter" +msgstr "Contador de acceso" + +#. module: vault_share +#: model:ir.actions.server,name:vault_share.cron_share_clean_ir_actions_server +#: model:ir.cron,cron_name:vault_share.cron_share_clean +#: model:ir.cron,name:vault_share.cron_share_clean +msgid "Clean outgoing share" +msgstr "Limpiar recurso compartido externo" + +#. module: vault_share +#: model:ir.model,name:vault_share.model_res_company +msgid "Companies" +msgstr "Compañías" + +#. module: vault_share +#: model:ir.model,name:vault_share.model_res_config_settings +msgid "Config Settings" +msgstr "Opciones de Configuración" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__create_uid +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__create_date +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form +msgid "Days" +msgstr "Días" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form +msgid "Delay the deletion of shares" +msgstr "Retrasar el borrado de recursos compartidos" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_res_config_settings__vault_share_delay +msgid "Delayed Deletion" +msgstr "Retrasar borrado" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_res_config_settings__vault_share_delay +msgid "" +"Delays the deletion of a share. After the expiration date it continues to " +"stay inaccessible" +msgstr "" +"Retrasa la eliminación de un recurso compartido. Después de la fecha de " +"caducidad sigue siendo inaccesible" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__display_name +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Enter the pin:" +msgstr "Ingresa el pin:" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__expiration +msgid "Expiration" +msgstr "Expiración" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__filename +msgid "Filename" +msgstr "Nombre del fichero" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__id +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__id +msgid "ID" +msgstr "ID" + +#. module: vault_share +#: code:addons/vault_share/controllers/main.py:0 +#, python-format +msgid "Invalid token" +msgstr "Token inválido" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__iv +msgid "Iv" +msgstr "Iv" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share____last_update +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__write_uid +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__write_uid +msgid "Last Updated by" +msgstr "Última modificación por" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__write_date +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__log_ids +msgid "Log" +msgstr "Registro" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__name +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__name +msgid "Name" +msgstr "Nombre" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#: model:ir.model.constraint,message:vault_share.constraint_vault_share_value_check +#, python-format +msgid "No value found" +msgstr "No se ha encontrado ningún valor" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__pin +msgid "Pin" +msgstr "Pin" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__salt +msgid "Salt" +msgstr "Sal" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#, python-format +msgid "Save in a vault" +msgstr "Guardar en un vault" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret +msgid "Secret" +msgstr "Secreto" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret_file +msgid "Secret File" +msgstr "Archivo de secretos" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__share_id +msgid "Share" +msgstr "Compartir" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__share_link +msgid "Share URL" +msgstr "Compartir url" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/legacy/vault_fields.js:0 +#, python-format +msgid "Share the secret" +msgstr "Compartir el secreto" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#, python-format +msgid "Share the secret with an external user" +msgstr "Compartir el secreto con usuario externo" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Shared file:" +msgstr "Archivo compartido:" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Shared secret:" +msgstr "Secreto compartido:" + +#. module: vault_share +#: model:ir.actions.act_window,name:vault_share.action_vault_share +#: model:ir.ui.menu,name:vault_share.menu_vault_share +msgid "Shares" +msgstr "Recursos compartidos" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__expiration +msgid "Specifies how long a share can be accessed until deletion." +msgstr "" +"Especifica el tiempo que se puede acceder a un recurso compartido hasta su " +"eliminación." + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__accesses +msgid "Specifies how often a share can be accessed before deletion." +msgstr "" +"Especifica la frecuencia con la que se puede acceder a un recurso compartido " +"antes de eliminarlo." + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__pin +msgid "The pin needed to decrypt the share." +msgstr "El pin necesario para descifrar el recurso compartido." + +#. module: vault_share +#: code:addons/vault_share/controllers/main.py:0 +#, python-format +msgid "The secret expired" +msgstr "El secreto ha expirado" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#, python-format +msgid "The share was accessed by %(name)s via %(ip)s" +msgstr "%(name)s ha accedido a la acción a través de %(ip)s" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#, python-format +msgid "The share was created by %(name)s" +msgstr "La acción fue creada por %(name)s" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__token +msgid "Token" +msgstr "Token" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__user_id +msgid "User" +msgstr "Usuario" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__share_link +msgid "Using this link and pin people can access the secret." +msgstr "Utilizando este enlace y el pin la gente puede acceder al secreto." + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_res_company__vault_share_delay +msgid "Vault Share Delay" +msgstr "Retraso de la Acción de la Bóveda" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share_log.py:0 +#: model:ir.model,name:vault_share.model_vault_share_log +#, python-format +msgid "Vault share log" +msgstr "Registro de compartición de la bóveda" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#: model:ir.model,name:vault_share.model_vault_share +#, python-format +msgid "Vault share outgoing secrets" +msgstr "La bóveda comparte secretos de salida" + +#, python-format +#~ msgid "The share was accessed by %s via %s" +#~ msgstr "El recurso compartido fue accedido por %s a través de %s" + +#, python-format +#~ msgid "The share was created by %s" +#~ msgstr "El recurso compartido fue creado por %s" diff --git a/vault_share/i18n/nl.po b/vault_share/i18n/nl.po new file mode 100644 index 0000000000..5ea11a1ac3 --- /dev/null +++ b/vault_share/i18n/nl.po @@ -0,0 +1,281 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * vault_share +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-05-05 13:33+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: none\n" +"Language: nl\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.14.1\n" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__accesses +msgid "Access counter" +msgstr "Toegangsteller" + +#. module: vault_share +#: model:ir.actions.server,name:vault_share.cron_share_clean_ir_actions_server +#: model:ir.cron,cron_name:vault_share.cron_share_clean +#: model:ir.cron,name:vault_share.cron_share_clean +msgid "Clean outgoing share" +msgstr "" + +#. module: vault_share +#: model:ir.model,name:vault_share.model_res_company +msgid "Companies" +msgstr "Bedrijven" + +#. module: vault_share +#: model:ir.model,name:vault_share.model_res_config_settings +msgid "Config Settings" +msgstr "Configuratie-instellingen" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__create_uid +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__create_uid +msgid "Created by" +msgstr "Aangemaakt door" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__create_date +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__create_date +msgid "Created on" +msgstr "Aangemaakt op" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form +msgid "Days" +msgstr "Dagen" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form +msgid "Delay the deletion of shares" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_res_config_settings__vault_share_delay +msgid "Delayed Deletion" +msgstr "Uitgestelde verwijdering" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_res_config_settings__vault_share_delay +msgid "" +"Delays the deletion of a share. After the expiration date it continues to " +"stay inaccessible" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__display_name +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__display_name +msgid "Display Name" +msgstr "Schermnaam" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Enter the pin:" +msgstr "Voer de pincode in:" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__expiration +msgid "Expiration" +msgstr "Vervaldatum" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__filename +msgid "Filename" +msgstr "Bestandsnaam" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__id +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__id +msgid "ID" +msgstr "ID" + +#. module: vault_share +#: code:addons/vault_share/controllers/main.py:0 +#, python-format +msgid "Invalid token" +msgstr "Ongeldige token" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__iv +msgid "Iv" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share____last_update +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log____last_update +msgid "Last Modified on" +msgstr "Laatst gewijzigd op" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__write_uid +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__write_uid +msgid "Last Updated by" +msgstr "Laatst bijgewerkt door" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__write_date +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__write_date +msgid "Last Updated on" +msgstr "Laatst bijgewerkt op" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__log_ids +msgid "Log" +msgstr "Log" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__name +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__name +msgid "Name" +msgstr "Naam" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#: model:ir.model.constraint,message:vault_share.constraint_vault_share_value_check +#, python-format +msgid "No value found" +msgstr "Geen waarde gevonden" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__pin +msgid "Pin" +msgstr "Pin" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__salt +msgid "Salt" +msgstr "" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#, python-format +msgid "Save in a vault" +msgstr "Opslaan in een kluis" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret +msgid "Secret" +msgstr "Geheim" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret_file +msgid "Secret File" +msgstr "Secret File" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__share_id +msgid "Share" +msgstr "Delen" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__share_link +msgid "Share URL" +msgstr "Share URL" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/legacy/vault_fields.js:0 +#, python-format +msgid "Share the secret" +msgstr "Deel het geheim" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#, python-format +msgid "Share the secret with an external user" +msgstr "Het geheim delen met een externe gebruiker" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Shared file:" +msgstr "Gedeeld bestand:" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Shared secret:" +msgstr "Gedeeld geheim:" + +#. module: vault_share +#: model:ir.actions.act_window,name:vault_share.action_vault_share +#: model:ir.ui.menu,name:vault_share.menu_vault_share +msgid "Shares" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__expiration +msgid "Specifies how long a share can be accessed until deletion." +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__accesses +msgid "Specifies how often a share can be accessed before deletion." +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__pin +msgid "The pin needed to decrypt the share." +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/controllers/main.py:0 +#, python-format +msgid "The secret expired" +msgstr "Het geheim is verlopen" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#, python-format +msgid "The share was accessed by %(name)s via %(ip)s" +msgstr "Het record werd geopend door %(name)s via %(ip)s" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#, python-format +msgid "The share was created by %(name)s" +msgstr "Het record is gemaakt door %(name)s" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__token +msgid "Token" +msgstr "Token" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__user_id +msgid "User" +msgstr "Gebruiker" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__share_link +msgid "Using this link and pin people can access the secret." +msgstr "Met deze link en pin kunnen mensen toegang krijgen tot het geheim." + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_res_company__vault_share_delay +msgid "Vault Share Delay" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share_log.py:0 +#: model:ir.model,name:vault_share.model_vault_share_log +#, python-format +msgid "Vault share log" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#: model:ir.model,name:vault_share.model_vault_share +#, python-format +msgid "Vault share outgoing secrets" +msgstr "" diff --git a/vault_share/i18n/vault_share.pot b/vault_share/i18n/vault_share.pot new file mode 100644 index 0000000000..9556dfab0d --- /dev/null +++ b/vault_share/i18n/vault_share.pot @@ -0,0 +1,282 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * vault_share +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__accesses +msgid "Access counter" +msgstr "" + +#. module: vault_share +#: model:ir.actions.server,name:vault_share.cron_share_clean_ir_actions_server +#: model:ir.cron,cron_name:vault_share.cron_share_clean +#: model:ir.cron,name:vault_share.cron_share_clean +msgid "Clean outgoing share" +msgstr "" + +#. module: vault_share +#: model:ir.model,name:vault_share.model_res_company +msgid "Companies" +msgstr "" + +#. module: vault_share +#: model:ir.model,name:vault_share.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__create_uid +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__create_uid +msgid "Created by" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__create_date +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__create_date +msgid "Created on" +msgstr "" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form +msgid "Days" +msgstr "" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form +msgid "Delay the deletion of shares" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_res_config_settings__vault_share_delay +msgid "Delayed Deletion" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_res_config_settings__vault_share_delay +msgid "" +"Delays the deletion of a share. After the expiration date it continues to " +"stay inaccessible" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__display_name +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__display_name +msgid "Display Name" +msgstr "" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Enter the pin:" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__expiration +msgid "Expiration" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__filename +msgid "Filename" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__id +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__id +msgid "ID" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/controllers/main.py:0 +#, python-format +msgid "Invalid token" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__iv +msgid "Iv" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share____last_update +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log____last_update +msgid "Last Modified on" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__write_uid +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__write_date +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__write_date +msgid "Last Updated on" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__log_ids +msgid "Log" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__name +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__name +msgid "Name" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#: model:ir.model.constraint,message:vault_share.constraint_vault_share_value_check +#, python-format +msgid "No value found" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__pin +msgid "Pin" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__salt +msgid "Salt" +msgstr "" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#, python-format +msgid "Save in a vault" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret +msgid "Secret" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret_file +msgid "Secret File" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__share_id +msgid "Share" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__share_link +msgid "Share URL" +msgstr "" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/legacy/vault_fields.js:0 +#, python-format +msgid "Share the secret" +msgstr "" + +#. module: vault_share +#. openerp-web +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#: code:addons/vault_share/static/src/backend/templates.xml:0 +#, python-format +msgid "Share the secret with an external user" +msgstr "" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Shared file:" +msgstr "" + +#. module: vault_share +#: model_terms:ir.ui.view,arch_db:vault_share.share +msgid "Shared secret:" +msgstr "" + +#. module: vault_share +#: model:ir.actions.act_window,name:vault_share.action_vault_share +#: model:ir.ui.menu,name:vault_share.menu_vault_share +msgid "Shares" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__expiration +msgid "Specifies how long a share can be accessed until deletion." +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__accesses +msgid "Specifies how often a share can be accessed before deletion." +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__pin +msgid "The pin needed to decrypt the share." +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/controllers/main.py:0 +#, python-format +msgid "The secret expired" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#, python-format +msgid "The share was accessed by %(name)s via %(ip)s" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#, python-format +msgid "The share was created by %(name)s" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__token +msgid "Token" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_vault_share__user_id +msgid "User" +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,help:vault_share.field_vault_share__share_link +msgid "Using this link and pin people can access the secret." +msgstr "" + +#. module: vault_share +#: model:ir.model.fields,field_description:vault_share.field_res_company__vault_share_delay +msgid "Vault Share Delay" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share_log.py:0 +#: model:ir.model,name:vault_share.model_vault_share_log +#, python-format +msgid "Vault share log" +msgstr "" + +#. module: vault_share +#: code:addons/vault_share/models/vault_share.py:0 +#: model:ir.model,name:vault_share.model_vault_share +#, python-format +msgid "Vault share outgoing secrets" +msgstr "" diff --git a/vault_share/migrations/16.0.1.0.0/post-migrate.py b/vault_share/migrations/16.0.1.0.0/post-migrate.py new file mode 100644 index 0000000000..15c3b22717 --- /dev/null +++ b/vault_share/migrations/16.0.1.0.0/post-migrate.py @@ -0,0 +1,13 @@ +# © 2024 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + # Before the migration the iterations were hardcoded to 4000 + _logger.info("Setting iterations for previous records") + + cr.execute("UPDATE vault_share SET iterations = 4000 WHERE iterations IS NULL") diff --git a/vault_share/models/__init__.py b/vault_share/models/__init__.py new file mode 100644 index 0000000000..e6b947e32a --- /dev/null +++ b/vault_share/models/__init__.py @@ -0,0 +1,4 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import res_company, res_config_settings, vault_share, vault_share_log diff --git a/vault_share/models/res_company.py b/vault_share/models/res_company.py new file mode 100644 index 0000000000..8eac8cd823 --- /dev/null +++ b/vault_share/models/res_company.py @@ -0,0 +1,10 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class RecCompany(models.Model): + _inherit = "res.company" + + vault_share_delay = fields.Integer(default=0) diff --git a/vault_share/models/res_config_settings.py b/vault_share/models/res_config_settings.py new file mode 100644 index 0000000000..4a69c1c18b --- /dev/null +++ b/vault_share/models/res_config_settings.py @@ -0,0 +1,24 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + vault_share_delay = fields.Integer( + string="Delayed Deletion", + related="company_id.vault_share_delay", + readonly=False, + help="Delays the deletion of a share. After the expiration date it continues " + "to stay inaccessible", + ) + + @api.onchange("vault_share_delay") + def _onchange_vault_share_delay(self): + self.vault_share_delay = max(0, self.vault_share_delay) diff --git a/vault_share/models/vault_share.py b/vault_share/models/vault_share.py new file mode 100644 index 0000000000..927e5e3269 --- /dev/null +++ b/vault_share/models/vault_share.py @@ -0,0 +1,86 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from datetime import datetime, timedelta +from uuid import uuid4 + +from odoo import _, api, fields, models + +_logger = logging.getLogger(__name__) + + +class VaultShare(models.Model): + _name = "vault.share" + _description = _("Vault share outgoing secrets") + + user_id = fields.Many2one("res.users", default=lambda self: self.env.uid) + name = fields.Char(required=True) + share_link = fields.Char( + "Share URL", + compute="_compute_url", + store=False, + help="Using this link and pin people can access the secret.", + ) + token = fields.Char(readonly=True, required=True, default=lambda self: uuid4()) + secret = fields.Char() + secret_file = fields.Char() + filename = fields.Char() + salt = fields.Char(required=True) + iterations = fields.Integer() + iv = fields.Char(required=True) + pin = fields.Char(required=True, help="The pin needed to decrypt the share.") + accesses = fields.Integer( + "Access counter", + default=5, + help="Specifies how often a share can be accessed before deletion.", + ) + expiration = fields.Datetime( + default=lambda self: datetime.now() + timedelta(days=7), + help="Specifies how long a share can be accessed until deletion.", + ) + log_ids = fields.One2many("vault.share.log", "share_id", "Log", readonly=True) + + _sql_constraints = [ + ( + "value_check", + "CHECK(secret IS NOT NULL OR secret_file IS NOT NULL)", + _("No value found"), + ), + ] + + @api.depends("token") + def _compute_url(self): + base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url") + for rec in self: + rec.share_link = f"{base_url}/vault/share/{rec.token}" + + @api.model + def get(self, token, ip=None): + rec = self.search([("token", "=", token)], limit=1) + if not rec: + return rec + + if datetime.now() < rec.expiration and rec.accesses > 0: + rec.accesses -= 1 + log = _("The share was accessed by %(name)s via %(ip)s") + rec.log_ids = [ + (0, 0, {"name": log % {"name": self.env.user.name, "ip": ip or "n/a"}}) + ] + return rec + + return None + + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + log = _("The share was created by %(name)s") + for rec in res: + rec.log_ids = [(0, 0, {"name": log % {"name": self.env.user.name}})] + return res + + @api.model + def clean(self): + now = datetime.now() + offset = timedelta(days=self.env.company.vault_share_delay) + self.search([("expiration", "<=", now + offset)]).unlink() diff --git a/vault_share/models/vault_share_log.py b/vault_share/models/vault_share_log.py new file mode 100644 index 0000000000..c8c2ef047e --- /dev/null +++ b/vault_share/models/vault_share_log.py @@ -0,0 +1,22 @@ +# © 2021 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import _, fields, models + +_logger = logging.getLogger(__name__) + + +class VaultShareLog(models.Model): + _name = "vault.share.log" + _description = _("Vault share log") + _order = "create_date DESC" + + share_id = fields.Many2one( + "vault.share", + ondelete="cascade", + readonly=True, + required=True, + ) + name = fields.Char(readonly=True) diff --git a/vault_share/readme/CONTRIBUTORS.rst b/vault_share/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..e202826121 --- /dev/null +++ b/vault_share/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Florian Kantelberg diff --git a/vault_share/readme/DESCRIPTION.rst b/vault_share/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..6348479690 --- /dev/null +++ b/vault_share/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module implements possibilities to share specific secrets with external users. This bases on the vault implementation and the generated RSA key pair. + +Share +~~~~~ + +This allows an user to share a secret with external users. A share can be generated from a vault entry or directly created by an user. The secret is symmetrically encrypted by a key derived from a pin. To grant access the user has to transmit the link and pin with the external. If either the access counter reaches 0 or the share expires it will be deleted automatically. Due to the usage of a numeric pin and the browser side decryption a share is vulnerable to brute-force attacks and shouldn't be used as a permanent storage for secrets. For long time uses the user should create an account and a vault should be used. diff --git a/vault_share/readme/ROADMAP.rst b/vault_share/readme/ROADMAP.rst new file mode 100644 index 0000000000..3a2ad3154c --- /dev/null +++ b/vault_share/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Secure the download of the encrypted file behind a challenge/response diff --git a/vault_share/security/ir.model.access.csv b/vault_share/security/ir.model.access.csv new file mode 100644 index 0000000000..54569a817c --- /dev/null +++ b/vault_share/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_vault_share,access_vault_share,model_vault_share,base.group_user,1,1,1,1 +access_vault_share_log,access_vault_share_log,model_vault_share_log,base.group_user,1,1,1,1 diff --git a/vault_share/security/ir_rule.xml b/vault_share/security/ir_rule.xml new file mode 100644 index 0000000000..e9a1dc2576 --- /dev/null +++ b/vault_share/security/ir_rule.xml @@ -0,0 +1,22 @@ + + + + vault.share.access.owner + + [('user_id', '=', user.id)] + + + + + + + + vault.share.log.access.owner + + [('share_id.user_id', '=', user.id)] + + + + + + diff --git a/vault_share/static/description/icon.png b/vault_share/static/description/icon.png new file mode 100644 index 0000000000..d79aa6f575 Binary files /dev/null and b/vault_share/static/description/icon.png differ diff --git a/vault_share/static/description/index.html b/vault_share/static/description/index.html new file mode 100644 index 0000000000..4f0f75df63 --- /dev/null +++ b/vault_share/static/description/index.html @@ -0,0 +1,420 @@ + + + + + + +Vault - Share + + + +
+

Vault - Share

+ + +

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runboat

+

This module implements possibilities to share specific secrets with external users. This bases on the vault implementation and the generated RSA key pair.

+
+

Share

+

This allows an user to share a secret with external users. A share can be generated from a vault entry or directly created by an user. The secret is symmetrically encrypted by a key derived from a pin. To grant access the user has to transmit the link and pin with the external. If either the access counter reaches 0 or the share expires it will be deleted automatically. Due to the usage of a numeric pin and the browser side decryption a share is vulnerable to brute-force attacks and shouldn’t be used as a permanent storage for secrets. For long time uses the user should create an account and a vault should be used.

+

Table of contents

+
+
+

Known issues / Roadmap

+
    +
  • Secure the download of the encrypted file behind a challenge/response
  • +
+
+
+

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

+
    +
  • initOS GmbH
  • +
+
+
+

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-auth project on GitHub.

+

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

+
+
+
+ + diff --git a/vault_share/static/src/backend/fields/templates.xml b/vault_share/static/src/backend/fields/templates.xml new file mode 100644 index 0000000000..90dab33f05 --- /dev/null +++ b/vault_share/static/src/backend/fields/templates.xml @@ -0,0 +1,46 @@ + + + + + !isNew + + + + + + isNew + + + + +
+ ******* +
+
+ + +
+
+ + + +