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

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");
}
};