Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by dreispt
- Loading branch information
Showing
91 changed files
with
6,717 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../vault |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../vault_share |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
:: | ||
|
||
┌───────┐ ┏━━━━━━━━━━━━━┓ ╔═══════════╗ | ||
│ input │ ┃ unencrypted ┃ ║ encrypted ║ | ||
└───────┘ ┗━━━━━━━━━━━━━┛ ╚═══════════╝ | ||
|
||
Vault | ||
===== | ||
|
||
Each vault stores entries with enrypted fields and files in a tree like structure. The access is controlled per vault. Every added user can read the secrets of a vault. Otherwise the users can receive permission to share the vault with other users, to write secrets in the vault, or to delete entries of the vault. The databases stores the public and password protected private key of each user. The password used for the private key is derived from a password entered by the user and should be different than the password used for the login. Keep in mind that the meta information like field name or file names aren't encrypted. | ||
|
||
Shared-key encryption | ||
===================== | ||
|
||
To be able to securely share sensitive data between all users a shared-key encryption is used. All users share a common secret for each vault. This secret is encrypted by the public key of each user to grant access to the user by using the private key to restore the secret. | ||
|
||
Encryption of master key | ||
------------------------ | ||
|
||
:: | ||
|
||
. ┏━━━━━━━━━━━━┓ | ||
┃ Master key ┃ | ||
┗━━━━━━━━━━━━┛ | ||
┏━━━━━━━━━━━━━━━━━┓ ┃ | ||
┃ User ┃ ▼ | ||
┃ ┃ ┏━━━━━━━━━┓ | ||
┃ ┏━━━━━━━━━━━━━┓ ┃ ┃ encrypt ┃ ╔════════════╗ | ||
┃ ┃ Public key ┃━━━━▶┃ (RSA) ┃━━━━━▶║ Master key ║ | ||
┃ ┗━━━━━━━━━━━━━┛ ┃ ┗━━━━━━━━━┛ ╚════════════╝ | ||
┃ ╔═════════════╗ ┃ | ||
┃ ║ Private key ║ ┃ | ||
┃ ╚═════════════╝ ┃ | ||
┗━━━━━━━━━━━━━━━━━┛ | ||
|
||
Decryption of master key | ||
------------------------ | ||
|
||
:: | ||
|
||
. ┌──────────┐ ┏━━━━━━━━━━┓ | ||
│ Password │━━━━▶┃ derive ┃ | ||
└──────────┘ ┃ (PBKDF2) ┃ | ||
┗━━━━━━━━━━┛ | ||
┃ | ||
┏━━━━━━━━━━━━━━━━━┓ ▼ ╔════════════╗ | ||
┃ User ┃ ┏━━━━━━━━━━┓ ║ Master key ║ | ||
┃ ┃ ┃ Password ┃ ╚════════════╝ | ||
┃ ┏━━━━━━━━━━━━━┓ ┃ ┗━━━━━━━━━━┛ ┃ | ||
┃ ┃ Public key ┃ ┃ ┃ ▼ | ||
┃ ┗━━━━━━━━━━━━━┛ ┃ ▼ ┏━━━━━━━━━┓ | ||
┃ ╔═════════════╗ ┃ ┏━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ ┃ decrypt ┃ ┏━━━━━━━━━━━━┓ | ||
┃ ║ Private key ║━━━━━┃ unlock ┃━━▶┃ Private key ┃━━━▶┃ (RSA) ┃━━━━━▶┃ Master key ┃ | ||
┃ ╚═════════════╝ ┃ ┗━━━━━━━━┛ ┗━━━━━━━━━━━━━┛ ┗━━━━━━━━━┛ ┗━━━━━━━━━━━━┛ | ||
┗━━━━━━━━━━━━━━━━━┛ | ||
|
||
Symmetric encryption of the data | ||
================================ | ||
|
||
The symmetric cipher AES is used with the common master key to encrypt/decrypt the secrets of the vaults. The encryption parameter and encrypted data is stored in the database while everything else happens in the browser. | ||
|
||
Encryption of data | ||
------------------ | ||
|
||
:: | ||
|
||
. ┏━━━━━━━━━━━━┓ | ||
┃ Master key ┃ | ||
┗━━━━━━━━━━━━┛ | ||
┃ ┏━━━━━━━━━━━━━━━━━━┓ | ||
▼ ┃ Database ┃ | ||
┏━━━━━━━━━┓ ┃ ┃ | ||
┏━━━━━━━━━━━━┓ ┃ encrypt ┃ ┃╔════════════════╗┃ | ||
┃ Plain text ┃━━▶┃ (AES) ┃━━━▶║ Encrypted data ║┃ | ||
┗━━━━━━━━━━━━┛ ┗━━━━━━━━━┛ ┃╚════════════════╝┃ | ||
┃ ┃┏━━━━━━━━━━━━━━━━┓┃ | ||
┗━━━━━━━━▶┃ Parameters ┃┃ | ||
┃┗━━━━━━━━━━━━━━━━┛┃ | ||
┗━━━━━━━━━━━━━━━━━━┛ | ||
|
||
Decryption of data | ||
------------------ | ||
|
||
:: | ||
|
||
. ┏━━━━━━━━━━━━┓ | ||
┃ Master key ┃ | ||
┗━━━━━━━━━━━━┛ | ||
┏━━━━━━━━━━━━━━━━━━┓ ┃ | ||
┃ Database ┃ ▼ | ||
┃ ┃ ┏━━━━━━━━━┓ | ||
┃╔════════════════╗┃ ┃ decrypt ┃ ┏━━━━━━━━━━━━┓ | ||
┃║ Encrypted data ║━━━▶┃ (AES) ┃━━▶┃ Plain text ┃ | ||
┃╚════════════════╝┃ ┗━━━━━━━━━┛ ┗━━━━━━━━━━━━┛ | ||
┃┏━━━━━━━━━━━━━━━━┓┃ ▲ | ||
┃┃ Parameters ┃━━━━━━━━┛ | ||
┃┗━━━━━━━━━━━━━━━━┛┃ | ||
┗━━━━━━━━━━━━━━━━━━┛ | ||
|
||
Inbox | ||
===== | ||
|
||
This allows an user to receive encrypted secrets by external or internal Odoo users. External users have to use either the owner specific inbox link from his preferences or the link of an already created inbox. The value is symmetrically encrypted. The key for the encryption is wrapped with the public key of the user of the inbox to grant the user the access to the key. Internal users can directly send a secret from a vault entry to another user who has enabled this feature. If a direct link is used the access counter and expiration time can block an overwrite. | ||
|
||
Encryption of inbox | ||
------------------- | ||
|
||
:: | ||
|
||
. ┏━━━━━━━━━━━━┓ | ||
┃ Plain data ┃ | ||
┗━━━━━━━━━━━━┛ | ||
┏━━━━━━━━━━━━━━━━━┓ ┃ | ||
┃ User ┃ ▼ | ||
┃ ┃ ┏━━━━━━━━━┓ | ||
┃ ┏━━━━━━━━━━━━━┓ ┃ ┃ encrypt ┃ ╔════════════════╗ | ||
┃ ┃ Public key ┃━━━━▶┃ (RSA) ┃━━━━━▶║ Encrypted data ║ | ||
┃ ┗━━━━━━━━━━━━━┛ ┃ ┗━━━━━━━━━┛ ╚════════════════╝ | ||
┃ ╔═════════════╗ ┃ | ||
┃ ║ Private key ║ ┃ | ||
┃ ╚═════════════╝ ┃ | ||
┗━━━━━━━━━━━━━━━━━┛ | ||
|
||
Decryption of inbox | ||
------------------- | ||
|
||
:: | ||
|
||
. ┌──────────┐ ┏━━━━━━━━━━┓ | ||
│ Password │━━━━▶┃ derive ┃ | ||
└──────────┘ ┃ (PBKDF2) ┃ | ||
┗━━━━━━━━━━┛ | ||
┃ | ||
┏━━━━━━━━━━━━━━━━━┓ ▼ ╔════════════════╗ | ||
┃ User ┃ ┏━━━━━━━━━━┓ ║ Encrypted data ║ | ||
┃ ┃ ┃ Password ┃ ╚════════════════╝ | ||
┃ ┏━━━━━━━━━━━━━┓ ┃ ┗━━━━━━━━━━┛ ┃ | ||
┃ ┃ Public key ┃ ┃ ┃ ▼ | ||
┃ ┗━━━━━━━━━━━━━┛ ┃ ▼ ┏━━━━━━━━━┓ | ||
┃ ╔═════════════╗ ┃ ┏━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ ┃ decrypt ┃ ┏━━━━━━━━━━━━┓ | ||
┃ ║ Private key ║━━━━━┃ unlock ┃━━▶┃ Private key ┃━━━▶┃ (RSA) ┃━━━━━▶┃ Plain data ┃ | ||
┃ ╚═════════════╝ ┃ ┗━━━━━━━━┛ ┗━━━━━━━━━━━━━┛ ┗━━━━━━━━━┛ ┗━━━━━━━━━━━━┛ | ||
┗━━━━━━━━━━━━━━━━━┛ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, wizards |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# © 2021 Florian Kantelberg - initOS GmbH | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
{ | ||
"name": "Vault", | ||
"summary": "Password vault integration in Odoo", | ||
"license": "AGPL-3", | ||
"version": "14.0.1.5.0", | ||
"website": "https://github.com/OCA/server-auth", | ||
"application": True, | ||
"author": "initOS GmbH, Odoo Community Association (OCA)", | ||
"category": "Vault", | ||
"depends": ["web"], | ||
"data": [ | ||
"security/ir.model.access.csv", | ||
"security/ir_rule.xml", | ||
"views/assets.xml", | ||
"views/res_users_views.xml", | ||
"views/vault_entry_views.xml", | ||
"views/vault_field_views.xml", | ||
"views/vault_file_views.xml", | ||
"views/vault_log_views.xml", | ||
"views/vault_inbox_views.xml", | ||
"views/vault_right_views.xml", | ||
"views/vault_views.xml", | ||
"views/menuitems.xml", | ||
"views/templates.xml", | ||
"wizards/vault_export_wizard.xml", | ||
"wizards/vault_import_wizard.xml", | ||
"wizards/vault_send_wizard.xml", | ||
"wizards/vault_store_wizard.xml", | ||
], | ||
"qweb": ["static/src/xml/templates.xml"], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# © 2021 Florian Kantelberg - initOS GmbH | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
from . import main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# © 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/inbox/<string:token>", type="http", auth="public") | ||
def vault_inbox(self, token): | ||
ctx = {"disable_footer": True, "token": token} | ||
# Find the right token | ||
inbox = request.env["vault.inbox"].sudo().find_inbox(token) | ||
user = request.env["res.users"].sudo().find_user_of_inbox(token) | ||
_logger.info("%s: %s", inbox, user) | ||
if len(inbox) == 1 and inbox.accesses > 0: | ||
ctx.update({"name": inbox.name, "public": inbox.user_id.active_key.public}) | ||
elif len(inbox) == 0 and len(user) == 1: | ||
ctx["public"] = user.active_key.public | ||
|
||
# A valid token would mean we found a public key | ||
if not ctx.get("public"): | ||
ctx["error"] = _("Invalid token") | ||
return request.render("vault.inbox", ctx) | ||
|
||
# Just render if GET method | ||
if request.httprequest.method != "POST": | ||
return request.render("vault.inbox", ctx) | ||
|
||
# Check the param | ||
name = request.params.get("name") | ||
secret = request.params.get("encrypted") | ||
secret_file = request.params.get("encrypted_file") | ||
filename = request.params.get("filename") | ||
iv = request.params.get("iv") | ||
key = request.params.get("key") | ||
if not name: | ||
ctx["error"] = _("Please specify a name") | ||
return request.render("vault.inbox", ctx) | ||
|
||
if not secret and not secret_file: | ||
ctx["error"] = _("No secret found") | ||
return request.render("vault.inbox", ctx) | ||
|
||
if secret_file and not filename: | ||
ctx["error"] = _("Missing filename") | ||
return request.render("vault.inbox", ctx) | ||
|
||
if not iv or not key: | ||
ctx["error"] = _("Something went wrong with the encryption") | ||
return request.render("vault.inbox", ctx) | ||
|
||
try: | ||
inbox.store_in_inbox(name, secret, secret_file, iv, key, user, filename) | ||
except Exception as e: | ||
_logger.exception(e) | ||
ctx["error"] = _( | ||
"An error occured. Please contact the user or administrator" | ||
) | ||
return request.render("vault.inbox", ctx) | ||
|
||
ctx["message"] = _("Successfully stored") | ||
return request.render("vault.inbox", ctx) | ||
|
||
@http.route("/vault/public", type="json") | ||
def vault_public(self, user_id): | ||
""" Get the public key of a specific user """ | ||
user = request.env["res.users"].sudo().browse(user_id).exists() | ||
if not user or not user.keys: | ||
return {} | ||
|
||
return {"public_key": user.active_keys.public} | ||
|
||
@http.route("/vault/keys/store", auth="user", type="json") | ||
def vault_store_keys(self, **kwargs): | ||
""" Store the key pair for the current user """ | ||
return request.env["res.users.key"].store(**kwargs) | ||
|
||
@http.route("/vault/keys/get", auth="user", type="json") | ||
def vault_get_keys(self): | ||
""" Get the currently active key pair """ | ||
return request.env.user.get_vault_keys() | ||
|
||
@http.route("/vault/rights/get", auth="user", type="json") | ||
def vault_get_right_keys(self): | ||
""" Get the master keys from the vault.right records """ | ||
rights = request.env.user.vault_right_ids | ||
return {right.vault_id.uuid: right.key for right in rights} | ||
|
||
@http.route("/vault/rights/store", auth="user", type="json") | ||
def vault_store_right_keys(self, keys): | ||
""" Store the master keys to the specific vault.right records """ | ||
if not isinstance(keys, dict): | ||
return | ||
|
||
for right in request.env.user.vault_right_ids: | ||
master_key = keys.get(right.vault_id.uuid) | ||
|
||
if isinstance(master_key, str): | ||
right.key = master_key |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# © 2021 Florian Kantelberg - initOS GmbH | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
from . import ( | ||
abstract_vault, | ||
abstract_vault_field, | ||
res_users, | ||
res_users_key, | ||
vault, | ||
vault_entry, | ||
vault_field, | ||
vault_file, | ||
vault_inbox, | ||
vault_log, | ||
vault_right, | ||
vault_tag, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# © 2021 Florian Kantelberg - initOS GmbH | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
import logging | ||
|
||
from odoo import _, api, models | ||
from odoo.exceptions import AccessError | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class AbstractVault(models.AbstractModel): | ||
"""Models must have the following fields: | ||
`perm_user`: The permissions are computed for this user | ||
`allowed_write`: The current user can read from the vault | ||
`allowed_write`: The current user has write access to the vault | ||
`allowed_share`: The current user can share the vault with other users | ||
`allowed_delete`: The current user can delete the vault or entries of it | ||
""" | ||
|
||
_name = "vault.abstract" | ||
_description = _("Abstract model to implement general access rights") | ||
|
||
@api.model | ||
def raise_access_error(self): | ||
raise AccessError( | ||
_( | ||
"The requested operation can not be completed due to security " | ||
"restrictions." | ||
) | ||
) | ||
|
||
def check_access_rule(self, operation): | ||
super().check_access_rule(operation) | ||
|
||
if self.env.su: | ||
return | ||
|
||
# We have to recompute if the user of the environment changed | ||
if self.env.user != self.mapped("perm_user"): | ||
vault = self if self._name == "vault" else self.mapped("vault_id") | ||
vault._compute_access() | ||
|
||
# Check the operation and matching permissions | ||
if operation == "read" and not self.filtered("allowed_read"): | ||
self.raise_access_error() | ||
|
||
if operation == "write" and not self.filtered("allowed_write"): | ||
self.raise_access_error() | ||
|
||
if operation == "unlink" and not self.filtered("allowed_delete"): | ||
self.raise_access_error() | ||
|
||
def _log_entry(self, msg, state): | ||
raise NotImplementedError() | ||
|
||
def log_entry(self, msg): | ||
return self._log_entry(msg, None) | ||
|
||
def log_info(self, msg): | ||
return self._log_entry(msg, "info") | ||
|
||
def log_warn(self, msg): | ||
return self._log_entry(msg, "warn") | ||
|
||
def log_error(self, msg): | ||
return self._log_entry(msg, "error") |
Oops, something went wrong.