Files
notesnook/packages/core/api/vault.js

222 lines
5.3 KiB
JavaScript
Raw Normal View History

2020-03-19 11:30:05 +05:00
import getId from "../utils/id";
export default class Vault {
/**
*
2020-04-16 02:36:09 +05:00
* @param {import('./index').default} db
*/
2020-04-16 02:36:09 +05:00
constructor(db) {
this._db = db;
this._context = db.context;
2020-03-08 11:33:55 +05:00
this._key = "Notesnook";
this._password = "";
this.ERRORS = {
noVault: "ERR_NO_VAULT",
vaultLocked: "ERR_VAULT_LOCKED",
wrongPassword: "ERR_WRONG_PASSWORD",
};
}
2020-04-16 02:36:09 +05:00
/**
* Creates a new vault (replacing if any older exists)
* @param {string} password The password
* @returns {Boolean}
*/
async create(password) {
2020-04-15 14:00:05 +05:00
const vaultKey = await this._context.read("vaultKey");
if (!vaultKey || !vaultKey.cipher || !vaultKey.iv) {
const encryptedData = await this._context.encrypt(
{ password },
this._key
);
2020-04-15 14:00:05 +05:00
await this._context.write("vaultKey", encryptedData);
2020-03-08 11:33:55 +05:00
this._password = password;
}
return true;
}
2020-04-16 02:36:09 +05:00
/**
* Unlocks the vault with the given password
* @param {string} password The password
* @throws ERR_NO_VAULT | ERR_WRONG_PASSWORD
* @returns {Boolean}
*/
async unlock(password) {
2020-04-15 14:00:05 +05:00
const vaultKey = await this._context.read("vaultKey");
2020-04-16 02:36:09 +05:00
if (!(await this._exists(vaultKey))) throw new Error(this.ERRORS.noVault);
var data;
try {
2020-04-15 14:00:05 +05:00
data = await this._context.decrypt({ password }, vaultKey);
} catch (e) {
2020-03-10 13:52:20 +05:00
throw new Error(this.ERRORS.wrongPassword);
}
2020-03-08 11:33:55 +05:00
if (data !== this._key) {
2020-03-10 13:52:20 +05:00
throw new Error(this.ERRORS.wrongPassword);
}
2020-03-08 11:33:55 +05:00
this._password = password;
return true;
}
2020-04-16 02:36:09 +05:00
/**
* Locks (add to vault) a note
* @param {string} noteId The id of the note to lock
*/
async add(noteId) {
await this._check();
2020-04-16 02:36:09 +05:00
const note = this._db.notes.note(noteId).data;
await this._lockNote(noteId, note);
}
2020-04-16 02:36:09 +05:00
/**
* Permanently unlocks (remove from vault) a note
* @param {string} noteId The note id
* @param {string} password The password to unlock note with
*/
async remove(noteId, password) {
if (await this.unlock(password)) {
2020-04-16 02:36:09 +05:00
const note = this._db.notes.note(noteId).data;
2020-03-08 11:33:55 +05:00
await this._unlockNote(note, true);
}
}
2020-04-16 02:36:09 +05:00
/**
* Temporarily unlock (open) a note
* @param {string} noteId The note id
* @param {string} password The password to open note with
*/
async open(noteId, password) {
if (await this.unlock(password)) {
2020-04-16 02:36:09 +05:00
const note = this._db.notes.note(noteId).data;
2020-03-08 11:33:55 +05:00
return this._unlockNote(note, false);
}
}
2020-04-16 02:36:09 +05:00
/**
* Saves a note into the vault
* @param {{Object}} note The note to save into the vault
*/
2020-03-08 11:33:55 +05:00
async save(note) {
if (!note) return;
await this._check();
2020-03-19 11:30:05 +05:00
let id = note.id || getId();
2020-03-08 11:33:55 +05:00
return await this._lockNote(id, note);
}
2020-04-16 02:36:09 +05:00
// Private & internal methods
/** @private */
async _exists(vaultKey) {
if (!vaultKey) vaultKey = await this._context.read("vaultKey");
return vaultKey && vaultKey.cipher && vaultKey.iv;
}
/** @private */
async _locked() {
return !this._password || !this._password.length;
}
/** @private */
async _check() {
if (!(await this._exists())) {
throw new Error(this.ERRORS.noVault);
}
if (await this._locked()) {
throw new Error(this.ERRORS.vaultLocked);
}
}
/** @private */
2020-03-19 11:30:05 +05:00
async _encryptContent(content, ids) {
let { text, delta } = { ...content };
let { deltaId, textId } = ids;
2020-03-08 11:33:55 +05:00
2020-03-19 11:30:05 +05:00
if (!delta.ops) delta = await this._db.delta.get(deltaId);
if (text === textId) text = await this._db.text.get(textId);
2020-03-08 11:33:55 +05:00
text = await this._context.encrypt({ password: this._password }, text);
2020-04-15 16:45:40 +05:00
delta = await this._context.encrypt(
{ password: this._password },
JSON.stringify(delta)
);
2020-03-19 11:30:05 +05:00
await this._db.text.add({ id: textId, data: text });
await this._db.delta.add({ id: deltaId, data: delta });
2020-03-08 11:33:55 +05:00
}
2020-04-16 02:36:09 +05:00
/** @private */
2020-03-19 11:30:05 +05:00
async _decryptContent(content) {
let { text, delta } = { ...content };
text = await this._db.text.get(text);
text = await this._context.decrypt({ password: this._password }, text);
2020-03-19 11:30:05 +05:00
delta = await this._db.text.get(delta);
delta = await this._context.decrypt({ password: this._password }, delta);
2020-03-19 11:30:05 +05:00
if (typeof delta === "string") delta = JSON.parse(delta);
2020-03-19 11:30:05 +05:00
return {
delta,
text,
2020-03-19 11:30:05 +05:00
};
2020-03-08 11:33:55 +05:00
}
2020-04-16 02:36:09 +05:00
/** @private */
2020-03-08 11:33:55 +05:00
async _lockNote(id, note) {
2020-04-15 17:16:13 +05:00
if (!note) return;
let oldNote = this._db.notes.note(id);
2020-03-19 11:30:05 +05:00
let deltaId = 0;
let textId = 0;
2020-04-15 17:16:13 +05:00
if (oldNote && oldNote.data.content) {
deltaId = oldNote.data.content.delta;
textId = oldNote.data.content.text;
2020-03-19 11:30:05 +05:00
}
2020-03-08 11:33:55 +05:00
2020-03-19 11:30:05 +05:00
await this._encryptContent(note.content, { textId, deltaId });
2020-03-08 11:33:55 +05:00
return await this._db.notes.add({
id,
locked: true,
2020-04-15 17:16:13 +05:00
headline: "",
2020-03-08 11:33:55 +05:00
});
}
2020-04-16 02:36:09 +05:00
/** @private */
2020-03-08 11:33:55 +05:00
async _unlockNote(note, perm = false) {
if (!note.locked) return;
2020-03-19 11:30:05 +05:00
let { delta, text } = await this._decryptContent(note.content);
2020-03-08 11:33:55 +05:00
if (perm) {
await this._db.notes.add({
id: note.id,
locked: false,
content: {
delta,
text,
},
2020-03-08 11:33:55 +05:00
});
2020-03-19 11:30:05 +05:00
return;
}
2020-03-08 11:33:55 +05:00
return {
...note,
content: { delta, text },
2020-03-08 11:33:55 +05:00
};
}
2020-04-16 02:36:09 +05:00
/** @inner */
async _getKey() {
if (await this._exists()) return await this._context.read("vaultKey");
}
/** @inner */
async _setKey(vaultKey) {
if (!vaultKey) return;
await this._context.write("vaultKey", vaultKey);
}
}