mirror of
https://github.com/bringout/oca-server-auth.git
synced 2026-04-19 17:52:04 +02:00
Initial commit: OCA Server Auth packages (29 packages)
This commit is contained in:
commit
3ed80311c4
1325 changed files with 127292 additions and 0 deletions
|
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="vault.Field.buttons.send" owl="1">
|
||||
<button
|
||||
t-if="sendButton"
|
||||
t-on-click="_onSendValue"
|
||||
class="btn btn-secondary btn-sm fa fa-share-alt o_vault_send"
|
||||
title="Send the secret to an user"
|
||||
aria-label="Send the secret to an user"
|
||||
/>
|
||||
</t>
|
||||
|
||||
<t t-name="vault.Field.buttons" owl="1">
|
||||
<button
|
||||
t-if="!state.decrypted && showButton"
|
||||
t-on-click="_onShowValue"
|
||||
class="btn btn-secondary btn-sm fa fa-eye o_vault_show"
|
||||
title="Show"
|
||||
aria-label="Show"
|
||||
/>
|
||||
<button
|
||||
t-elif="showButton"
|
||||
t-on-click="_onShowValue"
|
||||
class="btn btn-secondary btn-sm fa fa-eye-slash o_vault_show"
|
||||
title="Hide"
|
||||
aria-label="Hide"
|
||||
/>
|
||||
<button
|
||||
t-if="copyButton"
|
||||
t-on-click="_onCopyValue"
|
||||
class="btn btn-secondary btn-sm fa fa-clipboard o_vault_clipboard"
|
||||
title="Copy to clipboard"
|
||||
aria-label="Copy to clipboard"
|
||||
/>
|
||||
<t t-call="vault.Field.buttons.send" />
|
||||
</t>
|
||||
|
||||
<t t-name="vault.FieldVault" owl="1">
|
||||
<div class="o_vault o_vault_error" t-if="!supported()">
|
||||
<span>*******</span>
|
||||
</div>
|
||||
<div class="o_vault" t-elif="props.readonly">
|
||||
<span class="o_vault_buttons">
|
||||
<t t-call="vault.Field.buttons" />
|
||||
</span>
|
||||
<span t-esc="formattedValue" t-ref="span" />
|
||||
</div>
|
||||
<div class="o_vault" t-else="">
|
||||
<span class="o_vault_buttons">
|
||||
<button
|
||||
t-if="generateButton"
|
||||
t-on-click="_onGenerateValue"
|
||||
class="btn btn-secondary btn-sm fa fa-lock o_vault_generate"
|
||||
title="Generate"
|
||||
aria-label="Generate"
|
||||
/>
|
||||
</span>
|
||||
<input class="o_input" type="text" t-esc="formattedValue" t-ref="input" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t
|
||||
t-name="vault.FileVault"
|
||||
t-inherit="web.BinaryField"
|
||||
t-inherit-mode="primary"
|
||||
owl="1"
|
||||
>
|
||||
<xpath expr="//button[hasclass('o_clear_file_button')]" position="after">
|
||||
<t t-call="vault.Field.buttons.send" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="vault.FieldVaultInbox" owl="1">
|
||||
<div class="o_vault o_vault_error" t-if="!supported()">
|
||||
<span>*******</span>
|
||||
</div>
|
||||
<div class="o_vault" t-elif="props.value">
|
||||
<span class="o_vault_buttons">
|
||||
<t t-call="vault.Field.buttons" />
|
||||
<button
|
||||
t-if="saveButton"
|
||||
t-on-click="_onSaveValue"
|
||||
class="btn btn-secondary btn-sm fa fa-save"
|
||||
title="Save in a vault"
|
||||
aria-label="Save in a vault"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span class="o_vault_inbox" t-esc="formattedValue" t-ref="span" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="vault.FileVaultInbox" owl="1">
|
||||
<div class="o_vault o_vault_error" t-if="!supported()">
|
||||
<span>*******</span>
|
||||
</div>
|
||||
<div class="o_vault" t-elif="props.value">
|
||||
<span class="o_vault_buttons">
|
||||
<button
|
||||
t-if="saveButton"
|
||||
t-on-click="_onSaveValue"
|
||||
class="btn btn-secondary btn-sm fa fa-save"
|
||||
title="Save in a vault"
|
||||
aria-label="Save in a vault"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<a class="o_form_uri" href="#" t-on-click.prevent="onFileDownload">
|
||||
<span class="fa fa-download me-2" />
|
||||
<t t-if="state.fileName" t-esc="state.fileName" />
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="vault.FileVaultExport" owl="1">
|
||||
<a class="o_form_uri" href="#" t-on-click.prevent="onFileDownload">
|
||||
<span class="fa fa-download me-2" />
|
||||
<t t-if="state.fileName" t-esc="state.fileName" />
|
||||
</a>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/** @odoo-module alias=vault.export.file **/
|
||||
// © 2021-2024 Florian Kantelberg - initOS GmbH
|
||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import {BinaryField} from "@web/views/fields/binary/binary_field";
|
||||
import Exporter from "vault.export";
|
||||
import VaultMixin from "vault.mixin";
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {downloadFile} from "@web/core/network/download";
|
||||
import {registry} from "@web/core/registry";
|
||||
import utils from "vault.utils";
|
||||
|
||||
export default class VaultExportFile extends VaultMixin(BinaryField) {
|
||||
/**
|
||||
* Call the exporter and download the finalized file
|
||||
*/
|
||||
async onFileDownload() {
|
||||
if (!this.props.value) {
|
||||
this.do_warn(
|
||||
_lt("Save As..."),
|
||||
_lt("The field is empty, there's nothing to save!")
|
||||
);
|
||||
} else if (utils.supported()) {
|
||||
const exporter = new Exporter();
|
||||
const content = JSON.stringify(
|
||||
await exporter.export(
|
||||
await this._getMasterKey(),
|
||||
this.state.fileName,
|
||||
this.props.value
|
||||
)
|
||||
);
|
||||
|
||||
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 blob = new Blob([arr]);
|
||||
await downloadFile(blob, this.state.fileName || "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VaultExportFile.template = "vault.FileVaultExport";
|
||||
|
||||
registry.category("fields").add("vault_export_file", VaultExportFile);
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/** @odoo-module alias=vault.field **/
|
||||
// © 2021-2024 Florian Kantelberg - initOS GmbH
|
||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import {Component, useEffect, useRef, useState} from "@odoo/owl";
|
||||
import {useBus, useService} from "@web/core/utils/hooks";
|
||||
import VaultMixin from "vault.mixin";
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {getActiveHotkey} from "@web/core/hotkeys/hotkey_service";
|
||||
import {registry} from "@web/core/registry";
|
||||
import utils from "vault.utils";
|
||||
|
||||
export default class VaultField extends VaultMixin(Component) {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
this.action = useService("action");
|
||||
this.input = useRef("input");
|
||||
this.span = useRef("span");
|
||||
this.state = useState({
|
||||
decrypted: false,
|
||||
decryptedValue: "",
|
||||
isDirty: false,
|
||||
lastSetValue: null,
|
||||
});
|
||||
|
||||
const self = this;
|
||||
useEffect(
|
||||
(inputEl) => {
|
||||
if (inputEl) {
|
||||
const onInput = self.onInput.bind(self);
|
||||
const onKeydown = self.onKeydown.bind(self);
|
||||
|
||||
inputEl.addEventListener("input", onInput);
|
||||
inputEl.addEventListener("keydown", onKeydown);
|
||||
return () => {
|
||||
inputEl.removeEventListener("input", onInput);
|
||||
inputEl.removeEventListener("keydown", onKeydown);
|
||||
};
|
||||
}
|
||||
},
|
||||
() => [self.input.el]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const isInvalid = self.props.record
|
||||
? self.props.record.isInvalid(self.props.name)
|
||||
: false;
|
||||
|
||||
if (self.input.el && !self.state.isDirty && !isInvalid) {
|
||||
Promise.resolve(self.getValue()).then((val) => {
|
||||
if (!self.input.el) return;
|
||||
|
||||
if (val) self.input.el.value = val;
|
||||
else if (val !== "")
|
||||
self.props.record.setInvalidField(self.props.name);
|
||||
});
|
||||
self.state.lastSetValue = self.input.el.value;
|
||||
}
|
||||
});
|
||||
|
||||
useBus(self.env.bus, "RELATIONAL_MODEL:WILL_SAVE_URGENTLY", () =>
|
||||
self.commitChanges(true)
|
||||
);
|
||||
useBus(self.env.bus, "RELATIONAL_MODEL:NEED_LOCAL_CHANGES", (ev) =>
|
||||
ev.detail.proms.push(self.commitChanges())
|
||||
);
|
||||
useBus(self.env.bus, "RELATIONAL_MODEL:ENCRYPT_FIELDS", () => {
|
||||
this.state.decrypted = false;
|
||||
this.showValue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a dialog to generate a new secret
|
||||
*
|
||||
* @param {Object} ev
|
||||
*/
|
||||
async _onGenerateValue(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
const password = await utils.generate_pass();
|
||||
await this.storeValue(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the secret to an inbox of an user
|
||||
*
|
||||
* @param {Object} ev
|
||||
*/
|
||||
async _onSendValue(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
await this.sendValue(this.props.value, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 of the field
|
||||
*
|
||||
* @returns decrypted value
|
||||
*/
|
||||
async getValue() {
|
||||
return await this._decrypt(this.props.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the value shown
|
||||
*/
|
||||
async showValue() {
|
||||
this.span.el.innerHTML = this.formattedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle input event and set the state to dirty
|
||||
*
|
||||
* @param {Object} ev
|
||||
*/
|
||||
onInput(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
this.state.isDirty = ev.target.value !== this.lastSetValue;
|
||||
if (this.props.setDirty) this.props.setDirty(this.state.isDirty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the changes of the input field to the record
|
||||
*
|
||||
* @param {Boolean} urgent
|
||||
*/
|
||||
async commitChanges(urgent) {
|
||||
if (!this.input.el) return;
|
||||
|
||||
this.state.isDirty = this.input.el.value !== this.lastSetValue;
|
||||
if (this.state.isDirty || urgent) {
|
||||
this.state.isDirty = false;
|
||||
|
||||
const val = this.input.el.value || false;
|
||||
if (val !== (this.state.lastSetValue || false)) {
|
||||
this.state.lastSetValue = this.input.el.value;
|
||||
this.state.decryptedValue = this.input.el.value;
|
||||
await this.storeValue(val);
|
||||
this.props.setDirty(this.state.isDirty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle keyboard events and trigger changes
|
||||
*
|
||||
* @param {Object} ev
|
||||
*/
|
||||
onKeydown(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
const hotkey = getActiveHotkey(ev);
|
||||
if (["enter", "tab", "shift+tab"].includes(hotkey)) this.commitChanges(false);
|
||||
}
|
||||
}
|
||||
|
||||
VaultField.displayName = _lt("Vault Field");
|
||||
VaultField.supportedTypes = ["char"];
|
||||
VaultField.template = "vault.FieldVault";
|
||||
|
||||
registry.category("fields").add("vault_field", VaultField);
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/** @odoo-module alias=vault.file **/
|
||||
// © 2021-2024 Florian Kantelberg - initOS GmbH
|
||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import {BinaryField} from "@web/views/fields/binary/binary_field";
|
||||
import VaultMixin from "vault.mixin";
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {downloadFile} from "@web/core/network/download";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import utils from "vault.utils";
|
||||
|
||||
export default class VaultFile extends VaultMixin(BinaryField) {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
this.action = useService("action");
|
||||
}
|
||||
|
||||
async update({data, name}) {
|
||||
const encrypted = await this._encrypt(data);
|
||||
return await super.update({data: encrypted, name: name});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the secret to an inbox of an user
|
||||
*
|
||||
* @param {Object} ev
|
||||
*/
|
||||
async _onSendValue(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
await this.sendValue("", this.props.value, this.state.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the file and download it
|
||||
*/
|
||||
async onFileDownload() {
|
||||
if (!this.props.value) {
|
||||
this.do_warn(
|
||||
_lt("Save As..."),
|
||||
_lt("The field is empty, there's nothing to save!")
|
||||
);
|
||||
} else if (utils.supported()) {
|
||||
const decrypted = await this._decrypt(this.props.value);
|
||||
const base64 = atob(decrypted);
|
||||
const buffer = new ArrayBuffer(base64.length);
|
||||
const arr = new Uint8Array(buffer);
|
||||
for (let i = 0; i < base64.length; i++) arr[i] = base64.charCodeAt(i);
|
||||
|
||||
const blob = new Blob([arr]);
|
||||
await downloadFile(blob, this.state.fileName || "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VaultFile.displayName = _lt("Vault File");
|
||||
VaultFile.template = "vault.FileVault";
|
||||
|
||||
registry.category("fields").add("vault_file", VaultFile);
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/** @odoo-module alias=vault.inbox.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 VaultInboxMixin from "vault.inbox.mixin";
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {registry} from "@web/core/registry";
|
||||
import utils from "vault.utils";
|
||||
import vault from "vault";
|
||||
|
||||
export default class VaultInboxField extends VaultInboxMixin(VaultField) {
|
||||
/**
|
||||
* Save the content in an entry of a vault
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _onSaveValue() {
|
||||
await this.saveValue("vault.field", this.props.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the data with the private key of the vault
|
||||
*
|
||||
* @private
|
||||
* @param {String} data
|
||||
* @returns the decrypted data
|
||||
*/
|
||||
async _decrypt(data) {
|
||||
if (!utils.supported()) return null;
|
||||
|
||||
const iv = this.props.record.data[this.props.fieldIV];
|
||||
const wrapped_key = this.props.record.data[this.props.fieldKey];
|
||||
|
||||
if (!iv || !wrapped_key) return false;
|
||||
|
||||
const key = await vault.unwrap(wrapped_key);
|
||||
return await utils.sym_decrypt(key, data, iv);
|
||||
}
|
||||
}
|
||||
|
||||
VaultInboxField.defaultProps = {
|
||||
...VaultField.defaultProps,
|
||||
fieldKey: "key",
|
||||
};
|
||||
VaultInboxField.displayName = _lt("Vault Inbox Field");
|
||||
VaultInboxField.template = "vault.FieldVaultInbox";
|
||||
|
||||
registry.category("fields").add("vault_inbox_field", VaultInboxField);
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/** @odoo-module alias=vault.inbox.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 VaultInboxMixin from "vault.inbox.mixin";
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {registry} from "@web/core/registry";
|
||||
import utils from "vault.utils";
|
||||
import vault from "vault";
|
||||
|
||||
export default class VaultInboxFile extends VaultInboxMixin(VaultFile) {
|
||||
/**
|
||||
* Save the content in an entry of a vault
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _onSaveValue() {
|
||||
await this.saveValue("vault.file", this.props.value, this.state.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the data with the private key of the vault
|
||||
*
|
||||
* @private
|
||||
* @param {String} data
|
||||
* @returns the decrypted data
|
||||
*/
|
||||
async _decrypt(data) {
|
||||
if (!utils.supported()) return null;
|
||||
|
||||
const iv = this.props.record.data[this.props.fieldIV];
|
||||
const wrapped_key = this.props.record.data[this.props.fieldKey];
|
||||
|
||||
if (!iv || !wrapped_key) return false;
|
||||
|
||||
const key = await vault.unwrap(wrapped_key);
|
||||
return await utils.sym_decrypt(key, data, iv);
|
||||
}
|
||||
}
|
||||
|
||||
VaultInboxFile.defaultProps = {
|
||||
...VaultFile.defaultProps,
|
||||
fieldKey: "key",
|
||||
};
|
||||
VaultInboxFile.displayName = _lt("Vault Inbox File");
|
||||
VaultInboxFile.template = "vault.FileVaultInbox";
|
||||
|
||||
registry.category("fields").add("vault_inbox_file", VaultInboxFile);
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/** @odoo-module alias=vault.inbox.mixin **/
|
||||
// © 2021-2024 Florian Kantelberg - initOS GmbH
|
||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import utils from "vault.utils";
|
||||
import vault from "vault";
|
||||
|
||||
export default (x) => {
|
||||
class Extended extends x {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
if (!this.action) this.action = useService("action");
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the content in an entry of a vault
|
||||
*
|
||||
* @private
|
||||
* @param {String} model
|
||||
* @param {String} value
|
||||
* @param {String} name
|
||||
*/
|
||||
async saveValue(model, value, name = "") {
|
||||
const key = await utils.generate_key();
|
||||
const iv = utils.generate_iv_base64();
|
||||
const decrypted = await this._decrypt(value);
|
||||
|
||||
this.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
title: _lt("Store the secret in a vault"),
|
||||
target: "new",
|
||||
res_model: "vault.store.wizard",
|
||||
views: [[false, "form"]],
|
||||
context: {
|
||||
default_model: model,
|
||||
default_secret_temporary: await utils.sym_encrypt(
|
||||
key,
|
||||
decrypted,
|
||||
iv
|
||||
),
|
||||
default_name: name,
|
||||
default_iv: iv,
|
||||
default_key: await vault.wrap(key),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Extended.props = {
|
||||
...x.props,
|
||||
storeModel: {type: String, optional: true},
|
||||
};
|
||||
|
||||
Extended.extractProps = ({attrs}) => {
|
||||
return {
|
||||
storeModel: attrs.store,
|
||||
};
|
||||
};
|
||||
|
||||
return Extended;
|
||||
};
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/** @odoo-module alias=vault.mixin **/
|
||||
// © 2021-2024 Florian Kantelberg - initOS GmbH
|
||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import {_lt} from "@web/core/l10n/translation";
|
||||
import {standardFieldProps} from "@web/views/fields/standard_field_props";
|
||||
import utils from "vault.utils";
|
||||
import vault from "vault";
|
||||
|
||||
export default (x) => {
|
||||
class Extended extends x {
|
||||
supported() {
|
||||
return utils.supported();
|
||||
}
|
||||
|
||||
// Control the visibility of the buttons
|
||||
get showButton() {
|
||||
return this.props.value;
|
||||
}
|
||||
get copyButton() {
|
||||
return this.props.value;
|
||||
}
|
||||
get sendButton() {
|
||||
return this.props.value;
|
||||
}
|
||||
get saveButton() {
|
||||
return this.props.value;
|
||||
}
|
||||
get generateButton() {
|
||||
return true;
|
||||
}
|
||||
get isNew() {
|
||||
return Boolean(this.model.record.isNew);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value by encrypting it
|
||||
*
|
||||
* @param {String} value
|
||||
* @param {Object} options
|
||||
*/
|
||||
async storeValue(value, options) {
|
||||
if (!utils.supported()) return;
|
||||
|
||||
const encrypted = await this._encrypt(value);
|
||||
await this.props.update(encrypted, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the value to an inbox
|
||||
*
|
||||
* @param {String} value_field
|
||||
* @param {String} value_file
|
||||
* @param {String} filename
|
||||
*/
|
||||
async sendValue(value_field = "", value_file = "", filename = "") {
|
||||
if (!utils.supported()) return;
|
||||
|
||||
if (!value_field && !value_file) return;
|
||||
|
||||
let enc_field = false,
|
||||
enc_file = false;
|
||||
|
||||
// Prepare the key and iv for the reencryption
|
||||
const key = await utils.generate_key();
|
||||
const iv = utils.generate_iv_base64();
|
||||
|
||||
// Reencrypt the field
|
||||
if (value_field) {
|
||||
const decrypted = await this._decrypt(value_field);
|
||||
enc_field = await utils.sym_encrypt(key, decrypted, iv);
|
||||
}
|
||||
|
||||
// Reencrypt the file
|
||||
if (value_file) {
|
||||
const decrypted = await this._decrypt(value_file);
|
||||
enc_file = await utils.sym_encrypt(key, decrypted, iv);
|
||||
}
|
||||
|
||||
// Call the wizard to handle the user selection and storage
|
||||
this.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
title: _lt("Send the secret to another user"),
|
||||
target: "new",
|
||||
res_model: "vault.send.wizard",
|
||||
views: [[false, "form"]],
|
||||
context: {
|
||||
default_secret: enc_field,
|
||||
default_secret_file: enc_file,
|
||||
default_filename: filename || false,
|
||||
default_iv: iv,
|
||||
default_key: await vault.wrap(key),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a different field
|
||||
*
|
||||
* @param {String} field
|
||||
* @param {String} value
|
||||
*/
|
||||
async _setFieldValue(field, value) {
|
||||
this.props.record.update({[field]: value});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the IV or generate a new one if needed
|
||||
*
|
||||
* @returns the IV to use
|
||||
*/
|
||||
async _getIV() {
|
||||
if (!utils.supported()) return null;
|
||||
|
||||
// Read the IV from the field
|
||||
let iv = this.props.record.data[this.props.fieldIV];
|
||||
if (iv) return iv;
|
||||
|
||||
// Generate a new IV
|
||||
iv = utils.generate_iv_base64();
|
||||
await this._setFieldValue(this.props.fieldIV, iv);
|
||||
return iv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the master key of the vault or generate a new one
|
||||
*
|
||||
* @returns the master key to use
|
||||
*/
|
||||
async _getMasterKey() {
|
||||
if (!utils.supported()) return null;
|
||||
|
||||
// Check if the master key is already extracted
|
||||
if (this.key) return await vault.unwrap(this.key);
|
||||
|
||||
// Get the wrapped master key from the field
|
||||
this.key = this.props.record.data[this.props.fieldKey];
|
||||
if (this.key) return await vault.unwrap(this.key);
|
||||
|
||||
// Generate a new master key and write it to the field
|
||||
const key = await utils.generate_key();
|
||||
this.key = await vault.wrap(key);
|
||||
await this._setFieldValue(this.props.fieldKey, this.key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data with the master key stored in the vault
|
||||
*
|
||||
* @param {String} data
|
||||
* @returns the decrypted data
|
||||
*/
|
||||
async _decrypt(data) {
|
||||
if (!utils.supported()) return null;
|
||||
|
||||
const iv = await this._getIV();
|
||||
const key = await this._getMasterKey();
|
||||
return await utils.sym_decrypt(key, data, iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt data with the master key stored in the vault
|
||||
*
|
||||
* @param {String} data
|
||||
* @returns the encrypted data
|
||||
*/
|
||||
async _encrypt(data) {
|
||||
if (!utils.supported()) return null;
|
||||
|
||||
const iv = await this._getIV();
|
||||
const key = await this._getMasterKey();
|
||||
return await utils.sym_encrypt(key, data, iv);
|
||||
}
|
||||
}
|
||||
|
||||
Extended.defaultProps = {
|
||||
...x.defaultProps,
|
||||
fieldIV: "iv",
|
||||
fieldKey: "master_key",
|
||||
};
|
||||
Extended.props = {
|
||||
...standardFieldProps,
|
||||
...x.props,
|
||||
fieldKey: {type: String, optional: true},
|
||||
fieldIV: {type: String, optional: true},
|
||||
};
|
||||
Extended.extractProps = ({attrs, field}) => {
|
||||
const extract_props = x.extractProps || (() => ({}));
|
||||
return {
|
||||
...extract_props({attrs, field}),
|
||||
fieldKey: attrs.key,
|
||||
fieldIV: attrs.iv,
|
||||
};
|
||||
};
|
||||
|
||||
return Extended;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue