Initial commit: OCA Server Auth packages (29 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:06 +02:00
commit 3ed80311c4
1325 changed files with 127292 additions and 0 deletions

View file

@ -0,0 +1,86 @@
=============
Vault - Share
=============
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:4f783283b6bf81c2c6575a505ca9e6d9f5d0ea120a8c48b2d2921596e51956f6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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 <https://github.com/OCA/server-auth/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 <https://github.com/OCA/server-auth/issues/new?body=module:%20vault_share%0Aversion:%2016.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
~~~~~~~
* initOS GmbH
Contributors
~~~~~~~~~~~~
* Florian Kantelberg <florian.kantelberg@initos.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-auth <https://github.com/OCA/server-auth/tree/16.0/vault_share>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

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

View file

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

View file

@ -0,0 +1,4 @@
# © 2021 Florian Kantelberg - initOS GmbH
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import main

View file

@ -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/<string:token>", 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)

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1">
<record id="cron_share_clean" model="ir.cron">
<field name="name">Clean outgoing share</field>
<field name="model_id" ref="model_vault_share" />
<field name="state">code</field>
<field name="code">model.clean()</field>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="active" eval="True" />
</record>
</odoo>

View file

@ -0,0 +1,298 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * vault_share
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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 "Brojač pristupa"
#. 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
msgid "Clean outgoing share"
msgstr "Obriši odlazno dijeljenje"
#. module: vault_share
#: model:ir.model,name:vault_share.model_res_company
msgid "Companies"
msgstr "Kompanije"
#. module: vault_share
#: model:ir.model,name:vault_share.model_res_config_settings
msgid "Config Settings"
msgstr "Postavke"
#. 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 "Kreirao"
#. 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 "Kreirano"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form
msgid "Days"
msgstr "Dani"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form
msgid "Delay the deletion of shares"
msgstr "Delay the deletion of shares"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_res_config_settings__vault_share_delay
msgid "Delayed Deletion"
msgstr "Odgođeno Brisanje"
#. 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 "Prikazani naziv"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.share
msgid "Enter the pin:"
msgstr "Enter the pin:"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__expiration
msgid "Expiration"
msgstr "Ističe"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__filename
msgid "Filename"
msgstr "Naziv datoteke"
#. 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
#. odoo-python
#: code:addons/vault_share/controllers/main.py:0
#, python-format
msgid "Invalid token"
msgstr "Nevažeći token"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__iterations
msgid "Iterations"
msgstr "Iteracije"
#. 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 "Zadnje mijenjano"
#. 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 "Zadnji ažurirao"
#. 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 "Zadnje ažurirano"
#. 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 "Naziv:"
#. module: vault_share
#. odoo-python
#: 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 "Vrijednost nije pronađena"
#. 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 "Salt"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret
msgid "Secret"
msgstr "Tajna"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret_file
msgid "Secret File"
msgstr "Tajna Datoteka"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__share_id
msgid "Share"
msgstr "Podijeli"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__share_link
msgid "Share URL"
msgstr "URL Dijeljenja"
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_field.esm.js:0
#, python-format
msgid "Share the secret"
msgstr "Share the secret"
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/templates.xml:0
#: code:addons/vault_share/static/src/backend/fields/templates.xml:0
#, python-format
msgid "Share the secret with an external user"
msgstr "Share the secret with an external user"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.share
msgid "Shared file:"
msgstr "Shared file:"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.share
msgid "Shared secret:"
msgstr "Shared secret:"
#. 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 "Dijeljenja"
#. 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 "Specificira koliko dugo dijeljenje može biti pristupno do brisanja."
#. 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 "Specificira koliko često dijeljenje može biti pristupno prije brisanja."
#. module: vault_share
#: model:ir.model.fields,help:vault_share.field_vault_share__pin
msgid "The pin needed to decrypt the share."
msgstr "PIN potreban za dešifriranje dijeljenja."
#. module: vault_share
#. odoo-python
#: code:addons/vault_share/controllers/main.py:0
#, python-format
msgid "The secret expired"
msgstr "The secret expired"
#. module: vault_share
#. odoo-python
#: code:addons/vault_share/models/vault_share.py:0
#, python-format
msgid "The share was accessed by %(name)s via %(ip)s"
msgstr "The share was accessed by %(name)s via %(ip)s"
#. module: vault_share
#. odoo-python
#: code:addons/vault_share/models/vault_share.py:0
#, python-format
msgid "The share was created by %(name)s"
msgstr "Dijeljenje je kreirao %(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 "Korisnik"
#. 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 "Koristeći ovaj link i PIN ljudi mogu pristupiti tajni."
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_pin_field.esm.js:0
#, python-format
msgid "Vault Pin Field"
msgstr "Polje PIN za Sef"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_res_company__vault_share_delay
msgid "Vault Share Delay"
msgstr "Kašnjenje Dijeljenja Sefa"
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_share_field.esm.js:0
#: code:addons/vault_share/static/src/backend/fields/vault_share_file.esm.js:0
#, python-format
msgid "Vault Share Field"
msgstr "Polje Dijeljenja Sefa"
#. module: vault_share
#. odoo-python
#: 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 "Zapisnik dijeljenja sefa"
#. module: vault_share
#. odoo-python
#: 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 "Odlazni tajni podaci dijeljenja sefa"

View file

@ -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 <informatica@totmaterial.es>\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"

View file

@ -0,0 +1,308 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * vault_share
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-03-08 15:34+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__accesses
msgid "Access counter"
msgstr "Contatore accessi"
#. 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
msgid "Clean outgoing share"
msgstr "Pulizia condivisione in uscita"
#. module: vault_share
#: model:ir.model,name:vault_share.model_res_company
msgid "Companies"
msgstr "Aziende"
#. module: vault_share
#: model:ir.model,name:vault_share.model_res_config_settings
msgid "Config Settings"
msgstr "Impostazioni configurazione"
#. 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 "Creato da"
#. 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 "Creato il"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form
msgid "Days"
msgstr "Giorni"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.res_config_settings_view_form
msgid "Delay the deletion of shares"
msgstr "Ritarda la cancellazione delle condivisioni"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_res_config_settings__vault_share_delay
msgid "Delayed Deletion"
msgstr "Cancellazioni ritardate"
#. 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 ""
"Ritarda la cancellazione di una condivisione. Dopo la scadenza continua ad "
"essere inaccessibile"
#. 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 "Nome visualizzato"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.share
msgid "Enter the pin:"
msgstr "Inserire il PIN:"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__expiration
msgid "Expiration"
msgstr "Scadenza"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__filename
msgid "Filename"
msgstr "Nome file"
#. 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
#. odoo-python
#: code:addons/vault_share/controllers/main.py:0
#, python-format
msgid "Invalid token"
msgstr "Token non valido"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__iterations
msgid "Iterations"
msgstr "Iterazioni"
#. 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 "Ultima modifica il"
#. 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 "Ultimo aggiornamento di"
#. 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 "Ultimo aggiornamento il"
#. 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 "Nome"
#. module: vault_share
#. odoo-python
#: 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 "Nessun valore trovato"
#. 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 "Salt"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret
msgid "Secret"
msgstr "Segreto"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__secret_file
msgid "Secret File"
msgstr "File segreto"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share_log__share_id
msgid "Share"
msgstr "Condividi"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_vault_share__share_link
msgid "Share URL"
msgstr "URL condivisione"
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_field.esm.js:0
#, python-format
msgid "Share the secret"
msgstr "Condividi il segreto"
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/templates.xml:0
#: code:addons/vault_share/static/src/backend/fields/templates.xml:0
#, python-format
msgid "Share the secret with an external user"
msgstr "Condividi il segreto con un utente esterno"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.share
msgid "Shared file:"
msgstr "File condiviso:"
#. module: vault_share
#: model_terms:ir.ui.view,arch_db:vault_share.share
msgid "Shared secret:"
msgstr "Segreto condiviso:"
#. 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 "Condivisioni"
#. 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 ""
"Indica per quanto tempo si può accedere ad una condivisione prima della "
"cancellazione."
#. 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 ""
"Indica quante volte si può accedere ad una condivisione prima della "
"cancellazione."
#. module: vault_share
#: model:ir.model.fields,help:vault_share.field_vault_share__pin
msgid "The pin needed to decrypt the share."
msgstr "Il PIN richiesto per decriptare la condivisione."
#. module: vault_share
#. odoo-python
#: code:addons/vault_share/controllers/main.py:0
#, python-format
msgid "The secret expired"
msgstr "Il segreto è scaduto"
#. module: vault_share
#. odoo-python
#: code:addons/vault_share/models/vault_share.py:0
#, python-format
msgid "The share was accessed by %(name)s via %(ip)s"
msgstr "La codivisione è stata utilizzata da %(name)s attraverso %(ip)s"
#. module: vault_share
#. odoo-python
#: code:addons/vault_share/models/vault_share.py:0
#, python-format
msgid "The share was created by %(name)s"
msgstr "La condivisione è stata creata da %(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 "Utente"
#. 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 ""
"Utilizzando questo collegamento e PIN le persone possono accedere al segreto."
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_pin_field.esm.js:0
#, python-format
msgid "Vault Pin Field"
msgstr "Campo PIN deposito di sicurezza"
#. module: vault_share
#: model:ir.model.fields,field_description:vault_share.field_res_company__vault_share_delay
msgid "Vault Share Delay"
msgstr "Ritardo condivisione deposito di sicurezza"
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_share_field.esm.js:0
#: code:addons/vault_share/static/src/backend/fields/vault_share_file.esm.js:0
#, python-format
msgid "Vault Share Field"
msgstr "Campo condivisione deposito di sicurezza"
#. module: vault_share
#. odoo-python
#: 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 "Log condivisione deposito di sicurezza"
#. module: vault_share
#. odoo-python
#: 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 "Segreti uscita condivisione deposito di sicurezza"

View file

@ -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 <c5e2fd43-d292-4c90-9d1f-74ff3436329a@anonaddy.me>\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 ""

View file

@ -0,0 +1,298 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * vault_share
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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
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
#. odoo-python
#: 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__iterations
msgid "Iterations"
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
#. odoo-python
#: 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
#: 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
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_field.esm.js:0
#, python-format
msgid "Share the secret"
msgstr ""
#. module: vault_share
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/templates.xml:0
#: code:addons/vault_share/static/src/backend/fields/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
#. odoo-python
#: code:addons/vault_share/controllers/main.py:0
#, python-format
msgid "The secret expired"
msgstr ""
#. module: vault_share
#. odoo-python
#: 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
#. odoo-python
#: 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
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_pin_field.esm.js:0
#, python-format
msgid "Vault Pin Field"
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
#. odoo-javascript
#: code:addons/vault_share/static/src/backend/fields/vault_share_field.esm.js:0
#: code:addons/vault_share/static/src/backend/fields/vault_share_file.esm.js:0
#, python-format
msgid "Vault Share Field"
msgstr ""
#. module: vault_share
#. odoo-python
#: 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
#. odoo-python
#: 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 ""

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1 @@
* Florian Kantelberg <florian.kantelberg@initos.com>

View file

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

View file

@ -0,0 +1 @@
* Secure the download of the encrypted file behind a challenge/response

View file

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_vault_share access_vault_share model_vault_share base.group_user 1 1 1 1
3 access_vault_share_log access_vault_share_log model_vault_share_log base.group_user 1 1 1 1

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="vault_share_owner" model="ir.rule">
<field name="name">vault.share.access.owner</field>
<field name="model_id" ref="vault_share.model_vault_share" />
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="perm_create" eval="1" />
<field name="perm_write" eval="1" />
<field name="perm_unlink" eval="1" />
<field name="perm_read" eval="1" />
</record>
<record id="vault_share_log_owner" model="ir.rule">
<field name="name">vault.share.log.access.owner</field>
<field name="model_id" ref="vault_share.model_vault_share_log" />
<field name="domain_force">[('share_id.user_id', '=', user.id)]</field>
<field name="perm_create" eval="1" />
<field name="perm_write" eval="1" />
<field name="perm_unlink" eval="1" />
<field name="perm_read" eval="1" />
</record>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View file

@ -0,0 +1,429 @@
<!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: https://docutils.sourceforge.io/" />
<title>Vault - Share</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/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: gray; } /* 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, pre.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="vault-share">
<h1 class="title">Vault - Share</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:4f783283b6bf81c2c6575a505ca9e6d9f5d0ea120a8c48b2d2921596e51956f6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" 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 image-reference" href="https://github.com/OCA/server-auth/tree/16.0/vault_share"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-vault_share"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-auth&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module implements possibilities to share specific secrets with external users. This bases on the vault implementation and the generated RSA key pair.</p>
<div class="section" id="share">
<h1>Share</h1>
<p>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 shouldnt be used as a permanent storage for secrets. For long time uses the user should create an account and a vault should be used.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-1">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a></li>
</ul>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-1">Known issues / Roadmap</a></h2>
<ul class="simple">
<li>Secure the download of the encrypted file behind a challenge/response</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-auth/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 to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-auth/issues/new?body=module:%20vault_share%0Aversion:%2016.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">
<h2><a class="toc-backref" href="#toc-entry-3">Credits</a></h2>
</div>
</div>
<div class="section" id="authors">
<h1>Authors</h1>
<ul class="simple">
<li>initOS GmbH</li>
</ul>
</div>
<div class="section" id="contributors">
<h1>Contributors</h1>
<ul class="simple">
<li>Florian Kantelberg &lt;<a class="reference external" href="mailto:florian.kantelberg&#64;initos.com">florian.kantelberg&#64;initos.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h1>Maintainers</h1>
<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-auth/tree/16.0/vault_share">OCA/server-auth</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>
</body>
</html>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates id="template" xml:space="preserve">
<t
t-name="vault.FieldShareVault"
t-inherit="vault.FieldVault"
t-inherit-mode="primary"
owl="1"
>
<xpath expr="//div[@t-elif='props.readonly']" position="attributes">
<attribute name="t-elif">!isNew</attribute>
</xpath>
</t>
<t
t-name="vault.FileShareVault"
t-inherit="web.BinaryField"
t-inherit-mode="primary"
owl="1"
>
<xpath expr="//t[@t-if='!props.readonly']" position="attributes">
<attribute name="t-if">isNew</attribute>
</xpath>
</t>
<t t-name="vault.FieldPinVault" owl="1">
<div class="o_vault o_vault_error" t-if="!supported()">
<span>*******</span>
</div>
<div class="o_vault" t-else="">
<t t-call="vault.Field.buttons" />
<span t-esc="formattedValue" t-ref="span" />
</div>
</t>
<t t-inherit="vault.FieldVault" t-inherit-mode="extension" owl="1">
<xpath expr="//span[hasclass('o_vault_buttons')]" position="inside">
<button
t-if="shareButton"
class="btn btn-secondary btn-sm fa fa-external-link o_vault_share"
title="Share the secret with an external user"
aria-label="Share the secret with an external user"
t-on-click="_onShareValue"
/>
</xpath>
</t>
</templates>

View file

@ -0,0 +1,49 @@
/** @odoo-module **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import VaultField from "vault.field";
import {_lt} from "@web/core/l10n/translation";
import {patch} from "@web/core/utils/patch";
import sh_utils from "vault.share.utils";
import utils from "vault.utils";
import vault from "vault";
// Extend the widget to share
patch(VaultField.prototype, "vault_share", {
get shareButton() {
return this.props.value;
},
/**
* Share the value for an external user
*
* @private
*/
async _onShareValue(ev) {
ev.stopPropagation();
const iv = await utils.generate_iv_base64();
const pin = sh_utils.generate_pin(sh_utils.PinSize);
const salt = utils.generate_bytes(utils.SaltLength).buffer;
const key = await utils.derive_key(pin, salt, utils.Derive.iterations);
const public_key = await vault.get_public_key();
const value = await this._decrypt(this.props.value);
this.action.doAction({
type: "ir.actions.act_window",
title: _lt("Share the secret"),
target: "new",
res_model: "vault.share",
views: [[false, "form"]],
context: {
default_secret: await utils.sym_encrypt(key, value, iv),
default_pin: await utils.asym_encrypt(
public_key,
pin + utils.generate_iv_base64()
),
default_iterations: utils.Derive.iterations,
default_iv: iv,
default_salt: utils.toBase64(salt),
},
});
},
});

View file

@ -0,0 +1,110 @@
/** @odoo-module **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import {Component, useRef, useState} from "@odoo/owl";
import VaultMixin from "vault.mixin";
import {_lt} from "@web/core/l10n/translation";
import {registry} from "@web/core/registry";
import sh_utils from "vault.share.utils";
import {useService} from "@web/core/utils/hooks";
import utils from "vault.utils";
import vault from "vault";
export default class VaultPinField extends VaultMixin(Component) {
setup() {
super.setup();
this.action = useService("action");
this.span = useRef("span");
this.state = useState({
decrypted: false,
decryptedValue: "",
});
this.context = this.env.searchModel.context;
this.props.readonly = true;
}
get sendButton() {
return false;
}
get generateButton() {
return false;
}
get saveButton() {
return false;
}
/**
* Get the decrypted value or a placeholder
*
* @returns the decrypted value or a placeholder
*/
get formattedValue() {
if (!this.props.value) return "";
if (this.state.decrypted) return this.state.decryptedValue || "*******";
return "*******";
}
/**
* Decrypt the value using the private key of the vault and slice it to
* the actual pin size because there is a salt following
*
* @private
* @param {String} data
* @returns the decrypted data
*/
async _decrypt(data) {
if (!data) return data;
const pin_size = this.context.pin_size || sh_utils.PinSize;
const private_key = await vault.get_private_key();
const plain = await utils.asym_decrypt(private_key, data);
return plain.slice(0, pin_size);
}
/**
* Copy the decrypted secret to the clipboard
*
* @param {Object} ev
*/
async _onCopyValue(ev) {
ev.stopPropagation();
const value = await this._decrypt(this.props.value);
await navigator.clipboard.writeText(value);
}
/**
* Toggle between visible and invisible secret
*
* @param {Object} ev
*/
async _onShowValue(ev) {
ev.stopPropagation();
this.state.decrypted = !this.state.decrypted;
if (this.state.decrypted) {
this.state.decryptedValue = await this._decrypt(this.props.value);
} else {
this.state.decryptedValue = "";
}
await this.showValue();
}
/**
* Update the value shown
*/
async showValue() {
this.span.el.innerHTML = this.formattedValue;
}
}
VaultPinField.displayName = _lt("Vault Pin Field");
VaultPinField.supportedTypes = ["char"];
VaultPinField.template = "vault.FieldPinVault";
registry.category("fields").add("vault_pin", VaultPinField);

View file

@ -0,0 +1,15 @@
/** @odoo-module alias=vault.share.field **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import VaultField from "vault.field";
import VaultShareMixin from "vault.share.mixin";
import {_lt} from "@web/core/l10n/translation";
import {registry} from "@web/core/registry";
export default class VaultShareField extends VaultShareMixin(VaultField) {}
VaultShareField.displayName = _lt("Vault Share Field");
VaultShareField.template = "vault.FieldShareVault";
registry.category("fields").add("vault_share_field", VaultShareField);

View file

@ -0,0 +1,15 @@
/** @odoo-module alias=vault.share.file **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import VaultFile from "vault.file";
import VaultShareMixin from "vault.share.mixin";
import {_lt} from "@web/core/l10n/translation";
import {registry} from "@web/core/registry";
export default class VaultShareFile extends VaultShareMixin(VaultFile) {}
VaultShareFile.displayName = _lt("Vault Share Field");
VaultShareFile.template = "vault.FileShareVault";
registry.category("fields").add("vault_share_file", VaultShareFile);

View file

@ -0,0 +1,164 @@
/** @odoo-module alias=vault.share.mixin **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import sh_utils from "vault.share.utils";
import utils from "vault.utils";
import vault from "vault";
export default (x) => {
class Extended extends x {
setup() {
super.setup();
this.context = this.env.searchModel.context;
}
get shareButton() {
return false;
}
get sendButton() {
return false;
}
get isNew() {
return this.props.record.isNew;
}
/**
* Encrypt the pin with a random salt to make it hard to guess him by
* encrypting every possilbilty with the public key. Store the pin in the
* proper field
*
* @private
* @param {String} pin
*/
async _storePin(pin) {
const salt = utils.generate_iv_base64();
const crypted_pin = await utils.asym_encrypt(
await vault.get_public_key(),
pin + salt
);
await this._setFieldValue(this.props.fieldPin, crypted_pin);
}
/**
* Get the iterations
*
* @returns iterations for the password derivation
*/
async _getIterations() {
const record = this.props.record;
if (!record) return utils.Derive.iterations;
const iterations = record.data.iterations || utils.Derive.iterations;
await this._setFieldValue(this.props.fieldIterations, iterations);
return iterations;
}
/**
* Returns the pin from the class, record data, or generate a new pin if
* none s currently available
*
* @private
* @returns the pin
*/
async _getPin() {
const record = this.props.record;
if (!record) return null;
const pin_size = this.context.pin_size || sh_utils.PinSize;
let pin = record.data[this.props.fieldPin];
if (pin) {
// Decrypt the pin and slice him to the configured pin size
const private_key = await vault.get_private_key();
const plain = await utils.asym_decrypt(private_key, pin);
if (!plain) return null;
pin = plain.slice(0, pin_size);
return pin;
}
// Generate a new pin and store it
pin = sh_utils.generate_pin(pin_size);
await this._storePin(pin);
return pin;
}
/**
* Returns the salt from the class, record data, or generate a new salt if
* none is currently available
*
* @private
* @returns the salt
*/
async _getSalt() {
const record = this.props.record;
if (!record) return null;
let salt = record.data[this.props.fieldSalt];
if (salt) return salt;
// Generate a new salt and store him
salt = utils.toBase64(utils.generate_bytes(utils.SaltLength).buffer);
await this._setFieldValue(this.props.fieldSalt, salt);
return salt;
}
/**
* Decrypt the encrypted data using the pin, IV and salt
*
* @private
* @param {String} crypted
* @returns the decrypted secret
*/
async _decrypt(crypted) {
if (!utils.supported()) return null;
if (crypted === false) return false;
if (!this.props.value) return this.props.value;
const iv = await this._getIV();
const pin = await this._getPin();
const salt = utils.fromBase64(await this._getSalt());
const iterations = await this._getIterations();
const key = await utils.derive_key(pin, salt, iterations);
return await utils.sym_decrypt(key, crypted, iv);
}
/**
* Encrypt the data using the pin, IV and salt
*
* @private
* @param {String} data
* @returns the encrypted secret
*/
async _encrypt(data) {
if (!utils.supported()) return null;
const iv = await this._getIV();
const pin = await this._getPin();
const salt = utils.fromBase64(await this._getSalt());
const iterations = await this._getIterations();
const key = await utils.derive_key(pin, salt, iterations);
return await utils.sym_encrypt(key, data, iv);
}
}
Extended.defaultProps = {
...x.defaultProps,
fieldPin: "pin",
fieldSalt: "salt",
fieldIterations: "iterations",
};
Extended.props = {
...x.props,
fieldIterations: {type: String, optional: true},
fieldPin: {type: String, optional: true},
fieldSalt: {type: String, optional: true},
};
return Extended;
};

View file

@ -0,0 +1,3 @@
.o_vault_share {
white-space: pre-wrap;
}

View file

@ -0,0 +1,16 @@
/** @odoo-module alias=vault.share.utils **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import utils from "vault.utils";
const PinSize = 5;
function generate_pin(pin_size) {
return utils.generate_secret(pin_size, "0123456789");
}
export default {
PinSize: PinSize,
generate_pin: generate_pin,
};

View file

@ -0,0 +1,90 @@
/** @odoo-module **/
// © 2021-2024 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import utils from "vault.utils";
const data = {};
// Find the elements in the document and check if they have values
function find_elements() {
for (const id of [
"encrypted",
"salt",
"iv",
"encrypted_file",
"filename",
"iterations",
])
if (!data[id]) {
const element = document.getElementById(id);
data[id] = element && element.value;
}
}
function toggle_alert(element, successful) {
if (element) {
element.classList.add(successful ? "alert-success" : "alert-danger");
element.classList.remove(successful ? "alert-danger" : "alert-success");
}
}
function show(selector) {
const element = document.querySelector(selector);
if (element) element.classList.remove("o_hidden");
}
function hide(selector) {
const element = document.querySelector(selector);
if (element) element.classList.add("o_hidden");
}
document.getElementById("pin").onchange = async function () {
if (!utils.supported()) return;
find_elements();
// Derive the key from the pin
const key = await utils.derive_key(
this.value,
utils.fromBase64(data.salt),
data.iterations
);
hide("#secret_group");
hide("#file_group");
const pin = document.getElementById("pin");
const secret = document.getElementById("secret");
const secret_file = document.getElementById("secret_file");
if (!secret && !secret_file) return;
// There is no secret to decrypt
if (!this.value) {
toggle_alert(pin, false);
return;
}
// Decrypt the data and show the value
if (data.encrypted) {
secret.value = await utils.sym_decrypt(key, data.encrypted, data.iv);
toggle_alert(pin, secret.value);
show("#secret_group");
}
if (data.encrypted_file) {
const content = atob(
await utils.sym_decrypt(key, data.encrypted_file, data.iv)
);
const buffer = new ArrayBuffer(content.length);
const arr = new Uint8Array(buffer);
for (let i = 0; i < content.length; i++) arr[i] = content.charCodeAt(i);
const file = new Blob([arr]);
secret_file.text = data.filename;
secret_file.setAttribute("href", window.URL.createObjectURL(file));
secret_file.setAttribute("download", data.filename);
toggle_alert(pin, content);
show("#file_group");
}
};

View file

@ -0,0 +1,4 @@
# © 2021 Florian Kantelberg - initOS GmbH
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_share

View file

@ -0,0 +1,78 @@
# © 2021 Florian Kantelberg - initOS GmbH
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import json
import logging
from datetime import datetime
from uuid import uuid4
from odoo.tests import TransactionCase
from odoo.tools import mute_logger
from odoo.addons.website.tools import MockRequest
from ..controllers import main
_logger = logging.getLogger(__name__)
class TestShare(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.user = cls.env.user
cls.vals = {
"name": f"Share {cls.user.name}",
"secret": "secret",
"salt": "sa17",
"iv": "1v",
"pin": "12345",
}
cls.share = cls.env["vault.share"].create(cls.vals)
@mute_logger("odoo.sql_db")
def test_share(self):
self.assertEqual(self.share, self.share.get(self.share.token))
self.assertIn(self.share.token, self.share.share_link)
# Nothing should happen
self.share.clean()
self.assertEqual(self.share, self.share.get(self.share.token))
# Deletion because of accesses
share = self.share.create(self.vals)
share.accesses = 0
self.assertEqual(None, share.get(share.token))
# Deletion because of expiration
share = self.share.create(self.vals)
share.expiration = datetime(1970, 1, 1)
self.assertEqual(None, share.get(share.token))
# Search for invalid token
self.assertEqual(share.browse(), share.get(uuid4()))
@mute_logger("odoo.sql_db")
def test_vault_share(self):
def return_context(template, context):
self.assertEqual(template, "vault_share.share")
return json.dumps(context)
def load(response):
return json.loads(response.data)
with MockRequest(self.env) as request_mock:
request_mock.render = return_context
controller = main.Controller()
response = load(controller.vault_share(""))
self.assertIn("error", response)
response = load(controller.vault_share(self.share.token))
self.assertEqual(response["salt"], self.share.salt)
self.share.accesses = 0
response = load(controller.vault_share(self.share.token))
self.assertIn("error", response)

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="action_vault_share" model="ir.actions.act_window">
<field name="name">Shares</field>
<field name="res_model">vault.share</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
id="menu_vault_share"
groups="base.group_user"
parent="vault.menu_vault"
action="action_vault_share"
sequence="45"
/>
</odoo>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="vault.res_config_settings_view_form" />
<field name="arch" type="xml">
<div id="vault_share" position="after">
<div class="col-xs-12 col-md-6 o_setting_box" id="vault_share">
<div class="o_setting_right_pane">
<div class="o_form_label">Delay the deletion of shares</div>
<field name="vault_share_delay" /> Days
</div>
</div>
</div>
</field>
</record>
</odoo>

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="share">
<t t-call="web.login_layout">
<t
t-call-assets="vault_share.assets_frontend"
t-css="false"
defer_load="True"
/>
<input type="hidden" id="encrypted" t-att-value="encrypted" />
<input type="hidden" id="encrypted_file" t-att-value="encrypted_file" />
<input type="hidden" id="filename" t-att-value="filename" />
<input type="hidden" id="salt" t-att-value="salt" />
<input type="hidden" id="iv" t-att-value="iv" />
<input type="hidden" id="iterations" t-att-value="iterations" />
<div class="form-group" t-if="not error">
<label for="pin">Enter the pin:</label>
<input type="text" id="pin" class="form-control col-12 alert-danger" />
</div>
<p class="alert alert-danger" t-if="error" role="alert" t-esc="error" />
<p
class="alert alert-success"
t-if="message"
role="status"
t-esc="message"
/>
<div id="secret_group" class="form-group o_hidden" t-if="encrypted">
<label for="secret">Shared secret:</label>
<input
type="text"
id="secret"
readonly="readonly"
class="col-12 form-control"
/>
</div>
<div id="file_group" class="form-group o_hidden" t-if="encrypted_file">
<label for="secret">Shared file:</label>
<div class="col-12">
<a href="" id="secret_file" class="form-control" />
</div>
</div>
</t>
</template>
</odoo>

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_vault_share_tree" model="ir.ui.view">
<field name="model">vault.share</field>
<field name="arch" type="xml">
<tree>
<field name="name" />
<field name="share_link" widget="url" />
</tree>
</field>
</record>
<record id="view_vault_share_form" model="ir.ui.view">
<field name="model">vault.share</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="token" invisible="1" />
<field name="iv" invisible="1" />
<field name="salt" invisible="1" />
<field name="iterations" invisible="1" />
<field name="filename" invisible="1" />
<field name="name" />
<field name="share_link" widget="url" />
<field name="pin" widget="vault_pin" />
<field name="expiration" />
<field name="accesses" />
<field name="secret" widget="vault_share_field" />
<field
name="secret_file"
filename="filename"
widget="vault_share_file"
/>
</group>
<label for="log_ids" />
<field name="log_ids" options="{'no_open': True}">
<tree>
<field name="name" />
<field name="create_date" />
</tree>
</field>
</sheet>
</form>
</field>
</record>
</odoo>