anything-llm/server/utils/EncryptionManager/index.js
Timothy Carambat dc4ad6b5a9
[BETA] Live document sync (#1719)
* wip bg workers for live document sync

* Add ability to re-embed specific documents across many workspaces via background queue
bgworkser is gated behind expieremental system setting flag that needs to be explictly enabled
UI for watching/unwatching docments that are embedded.
TODO: UI to easily manage all bg tasks and see run results
TODO: UI to enable this feature and background endpoints to manage it

* create frontend views and paths
Move elements to correct experimental scope

* update migration to delete runs on removal of watched document

* Add watch support to YouTube transcripts (#1716)

* Add watch support to YouTube transcripts
refactor how sync is done for supported types

* Watch specific files in Confluence space (#1718)

Add failure-prune check for runs

* create tmp workflow modifications for beta image

* create tmp workflow modifications for beta image

* create tmp workflow modifications for beta image

* dual build
update copy of alert modals

* update job interval

* Add support for live-sync of Github files

* update copy for document sync feature

* hide Experimental features from UI

* update docs links

* [FEAT] Implement new settings menu for experimental features (#1735)

* implement new settings menu for experimental features

* remove unused context save bar

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* dont run job on boot

* unset workflow changes

* Add persistent encryption service
Relay key to collector so persistent encryption can be used
Encrypt any private data in chunkSources used for replay during resync jobs

* update jsDOC

* Linting and organization

* update modal copy for feature

---------

Co-authored-by: Sean Hatfield <seanhatfield5@gmail.com>
2024-06-21 13:38:50 -07:00

86 lines
2.8 KiB
JavaScript

const crypto = require("crypto");
const { dumpENV } = require("../helpers/updateENV");
// Class that is used to arbitrarily encrypt/decrypt string data via a persistent passphrase/salt that
// is either user defined or is created and saved to the ENV on creation.
class EncryptionManager {
#keyENV = "SIG_KEY";
#saltENV = "SIG_SALT";
#encryptionKey;
#encryptionSalt;
constructor({ key = null, salt = null } = {}) {
this.#loadOrCreateKeySalt(key, salt);
this.key = crypto.scryptSync(this.#encryptionKey, this.#encryptionSalt, 32);
this.algorithm = "aes-256-cbc";
this.separator = ":";
// Used to send key to collector process to be able to decrypt data since they do not share ENVs
// this value should use the CommunicationKey.encrypt process before sending anywhere outside the
// server process so it is never sent in its raw format.
this.xPayload = this.key.toString("base64");
}
log(text, ...args) {
console.log(`\x1b[36m[EncryptionManager]\x1b[0m ${text}`, ...args);
}
#loadOrCreateKeySalt(_key = null, _salt = null) {
if (!!_key && !!_salt) {
this.log(
"Pre-assigned key & salt for encrypting arbitrary data was used."
);
this.#encryptionKey = _key;
this.#encryptionSalt = _salt;
return;
}
if (!process.env[this.#keyENV] || !process.env[this.#saltENV]) {
this.log("Self-assigning key & salt for encrypting arbitrary data.");
process.env[this.#keyENV] = crypto.randomBytes(32).toString("hex");
process.env[this.#saltENV] = crypto.randomBytes(32).toString("hex");
if (process.env.NODE_ENV === "production") dumpENV();
} else
this.log("Loaded existing key & salt for encrypting arbitrary data.");
this.#encryptionKey = process.env[this.#keyENV];
this.#encryptionSalt = process.env[this.#saltENV];
return;
}
encrypt(plainTextString = null) {
try {
if (!plainTextString)
throw new Error("Empty string is not valid for this method.");
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
const encrypted = cipher.update(plainTextString, "utf8", "hex");
return [
encrypted + cipher.final("hex"),
Buffer.from(iv).toString("hex"),
].join(this.separator);
} catch (e) {
this.log(e);
return null;
}
}
decrypt(encryptedString) {
try {
const [encrypted, iv] = encryptedString.split(this.separator);
if (!iv) throw new Error("IV not found");
const decipher = crypto.createDecipheriv(
this.algorithm,
this.key,
Buffer.from(iv, "hex")
);
return decipher.update(encrypted, "hex", "utf8") + decipher.final("utf8");
} catch (e) {
this.log(e);
return null;
}
}
}
module.exports = { EncryptionManager };