Skip to content

Commit

Permalink
Module vault and vault_share
Browse files Browse the repository at this point in the history
  • Loading branch information
fkantelberg committed Feb 2, 2024
1 parent f56d371 commit 4f8b4a3
Show file tree
Hide file tree
Showing 40 changed files with 1,776 additions and 1,887 deletions.
24 changes: 21 additions & 3 deletions vault/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"name": "Vault",
"summary": "Password vault integration in Odoo",
"license": "AGPL-3",
"version": "14.0.1.8.0",
"version": "15.0.1.6.1",
"website": "https://github.com/OCA/server-auth",
"application": True,
"author": "initOS GmbH, Odoo Community Association (OCA)",
Expand All @@ -14,7 +14,6 @@
"data": [
"security/ir.model.access.csv",
"security/ir_rule.xml",
"views/assets.xml",
"views/res_config_settings_views.xml",
"views/res_users_views.xml",
"views/vault_entry_views.xml",
Expand All @@ -31,5 +30,24 @@
"wizards/vault_send_wizard.xml",
"wizards/vault_store_wizard.xml",
],
"qweb": ["static/src/xml/templates.xml"],
"assets": {
"vault.assets_frontend": [
"vault/static/src/common/*.js",
"vault/static/src/frontend/*.js",
],
"web.assets_backend": [
"vault/static/lib/**/*.min.js",
"vault/static/src/common/*.js",
"vault/static/src/backend/*.scss",
"vault/static/src/backend/*.js",
"vault/static/src/legacy/vault_controller.js",
"vault/static/src/legacy/vault_widget.js",
],
"web.assets_qweb": [
"vault/static/src/**/*.xml",
],
"web.tests_assets": [
"vault/static/tests/**/*.js",
],
},
}
12 changes: 0 additions & 12 deletions vault/i18n/vault.pot
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,6 @@ msgstr ""

#. module: vault
#: model:ir.actions.act_window,name:vault.action_vault_inbox
#: model:ir.model.fields,field_description:vault.field_res_users__inbox_ids
#: model:ir.model.fields,field_description:vault.field_vault_inbox_log__inbox_id
#: model:ir.ui.menu,name:vault.menu_vault_inbox
msgid "Inbox"
Expand Down Expand Up @@ -619,11 +618,6 @@ msgstr ""
msgid "Invalid token"
msgstr ""

#. module: vault
#: model_terms:ir.ui.view,arch_db:vault.view_users_form_keys_modif
msgid "Invalidate private key"
msgstr ""

#. module: vault
#: model:ir.model.fields,field_description:vault.field_res_users_key__iterations
msgid "Iterations"
Expand Down Expand Up @@ -1319,12 +1313,6 @@ msgid ""
" If an user is not showing please ask him to generate these."
msgstr ""

#. module: vault
#: model_terms:ir.ui.view,arch_db:vault.view_users_form_keys_modif
msgid ""
"You will loose access to all vaults and your inbox. Do you want to continue?"
msgstr ""

#. module: vault
#. openerp-web
#: code:addons/vault/static/src/xml/templates.xml:0
Expand Down
13 changes: 8 additions & 5 deletions vault/models/vault_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class VaultEntry(models.Model):
compute="_compute_complete_name",
store=True,
readonly=True,
recursive=True,
)
uuid = fields.Char(default=lambda self: uuid4(), required=True)
name = fields.Char(required=True)
Expand Down Expand Up @@ -81,7 +82,6 @@ def search_panel_select_range(self, field_name, **kwargs):
- from_search_panel: It will be used to overwrite domain.
Remove the limit of records (default is 200).
"""
kwargs.update(limit=False)
return super(
VaultEntry,
self.with_context(from_search_panel=True, entry_short_name=True),
Expand All @@ -94,10 +94,9 @@ def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None
domain = domain if domain else []
if self.env.context.get("from_search_panel"):
domain += [("child_ids", "!=", False)]
res = super().search_read(
return super().search_read(
domain=domain, fields=fields, offset=offset, limit=limit, order=order
)
return res

@api.depends("name", "complete_name")
def _compute_display_name(self):
Expand All @@ -124,8 +123,12 @@ def _search_expired(self, operator, value):
def log_change(self, action):
self.ensure_one()
self.log_info(
_("%s entry %s by %s")
% (action, self.complete_name, self.env.user.display_name)
_("%(action)s entry %(name)s by %(user)s")
% {
"action": action,
"name": self.complete_name,
"user": self.env.user.display_name,
}
)

@api.model_create_single
Expand Down
8 changes: 3 additions & 5 deletions vault/models/vault_inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ def store_in_inbox(
filename,
ip=None,
):
log_info = {"name": user.name, "ip": ip or "n/a"}
if len(self) == 0:
log = _("Created by %s via %s") % (user.name, ip or "n/a")
log = _("Created by %(name)s via %(ip)s") % log_info
return self.create(
{
"name": name,
Expand All @@ -97,10 +98,7 @@ def store_in_inbox(

self.ensure_one()
if self.accesses > 0 and datetime.now() < self.expiration:
log = _("Written by %s via %s") % (
self.env.user.name,
ip or "n/a",
)
log = _("Written by %(name)s via %(ip)s") % log_info

self.write(
{
Expand Down
128 changes: 128 additions & 0 deletions vault/static/src/backend/export.esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/** @odoo-module alias=vault.export **/
// © 2021-2022 Florian Kantelberg - initOS GmbH
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import {_t} from "web.core";
import utils from "vault.utils";

// This class handles the export to different formats by using a standardize
// JSON formatted data generated by the python backend.
//
// JSON format description:
//
// Entries are represented as objects with the following attributes
// `name`, `uuid`, `url`, `note`
// Specific fields of the entry. `uuid` is used for updating existing records
// `childs`
// Child entries
// `fields`, `files`
// List of encypted fields/files with `name`, `iv`, and `value`
//
export default class VaultExporter {
/**
* Encrypt a field of the above format properly for the backend to store.
* The changes are done inplace.
*
* @private
* @param {CryptoKey} master_key
* @param {Object} node
*/
async _export_json_entry(master_key, node) {
const fields = [];
for (const field of node.fields || [])
fields.push({
name: field.name,
value: await utils.sym_decrypt(master_key, field.value, field.iv),
});

const files = [];
for (const file of node.files || [])
files.push({
name: file.name,
value: await utils.sym_decrypt(master_key, file.value, file.iv),
});

const childs = [];
for (const entry of node.childs || [])
childs.push(await this._export_json_entry(master_key, entry));

return {
name: node.name || "",
uuid: node.uuid || null,
url: node.url || "",
note: node.note || "",
childs: childs,
fields: fields,
files: files,
};
}

/**
* Decrypt the data fro the JSON export.
*
* @private
* @param {CryptoKey} master_key
* @param {Object} data
* @returns the encrypted entry for the database
*/
async _export_json_data(master_key, data) {
const result = [];
for (const node of data)
result.push(await this._export_json_entry(master_key, node));
return JSON.stringify(result);
}

/**
* Export using JSON format. The database is stored in the `data` field of the JSON
* type and is an encrypted JSON object. For the encryption the needed encryption
* parameter `iv`, `salt` and `iterations` are stored in the file.
* This will add `iv` to fields and files and encrypt the `value`
*
* @private
* @param {CryptoKey} master_key
* @param {String} data
* @returns the encrypted entry for the database
*/
async _export_json(master_key, data) {
// Get the password for the exported file from the user
const askpass = await utils.askpass(
_t("Please enter the password for the database")
);
let password = askpass.password || "";
if (askpass.keyfile)
password += await utils.digest(utils.toBinary(askpass.keyfile));

const iv = utils.generate_iv_base64();
const salt = utils.generate_bytes(utils.SaltLength).buffer;
const iterations = 4000;
const key = await utils.derive_key(password, salt, iterations);

// Unwrap the master key and decrypt the entries
const content = await this._export_json_data(master_key, JSON.parse(data));
return {
type: "encrypted",
iv: iv,
salt: utils.toBase64(salt),
data: await utils.sym_encrypt(key, content, iv),
iterations: iterations,
};
}

/**
* The main export functions which checks the file ending and calls the right function
* to handle the rest of the export
*
* @private
* @param {CryptoKey} master_key
* @param {String} filename
* @param {String} content
* @returns the data importable by the backend or false on error
*/
async export(master_key, filename, content) {
if (!utils.supported()) return false;

if (filename.endsWith(".json"))
return await this._export_json(master_key, content);
return false;
}
}
Loading

0 comments on commit 4f8b4a3

Please sign in to comment.