mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
refactoring / unification of note encryption / decryption
This commit is contained in:
parent
18709eb340
commit
74fff39c3f
@ -4,7 +4,6 @@ const express = require('express');
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const auth = require('../../services/auth');
|
const auth = require('../../services/auth');
|
||||||
const data_encryption = require('../../services/data_encryption');
|
|
||||||
const protected_session = require('../../services/protected_session');
|
const protected_session = require('../../services/protected_session');
|
||||||
const sync_table = require('../../services/sync_table');
|
const sync_table = require('../../services/sync_table');
|
||||||
const wrap = require('express-promise-wrap').wrap;
|
const wrap = require('express-promise-wrap').wrap;
|
||||||
@ -12,15 +11,7 @@ const wrap = require('express-promise-wrap').wrap;
|
|||||||
router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||||
const noteId = req.params.noteId;
|
const noteId = req.params.noteId;
|
||||||
const history = await sql.getAll("SELECT * FROM notes_history WHERE note_id = ? order by date_modified_to desc", [noteId]);
|
const history = await sql.getAll("SELECT * FROM notes_history WHERE note_id = ? order by date_modified_to desc", [noteId]);
|
||||||
|
protected_session.decryptNoteHistoryRows(req, history);
|
||||||
const dataKey = protected_session.getDataKey(req);
|
|
||||||
|
|
||||||
for (const hist of history) {
|
|
||||||
if (hist.is_protected) {
|
|
||||||
hist.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(hist.note_history_id), hist.note_title);
|
|
||||||
hist.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(hist.note_history_id), hist.note_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(history);
|
res.send(history);
|
||||||
}));
|
}));
|
||||||
|
@ -24,12 +24,7 @@ router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
|||||||
return res.status(404).send({});
|
return res.status(404).send({});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detail.is_protected) {
|
protected_session.decryptNote(req, detail);
|
||||||
const dataKey = protected_session.getDataKey(req);
|
|
||||||
|
|
||||||
detail.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(detail.note_id), detail.note_title);
|
|
||||||
detail.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(detail.note_id), detail.note_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
detail: detail
|
detail: detail
|
||||||
|
@ -7,7 +7,6 @@ const wrap = require('express-promise-wrap').wrap;
|
|||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const protected_session = require('../../services/protected_session');
|
const protected_session = require('../../services/protected_session');
|
||||||
const data_encryption = require('../../services/data_encryption');
|
|
||||||
|
|
||||||
router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => {
|
router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||||
log.info('Executing script: ' + req.body.script);
|
log.info('Executing script: ' + req.body.script);
|
||||||
@ -22,21 +21,21 @@ router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => {
|
|||||||
router.get('/subtree/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
router.get('/subtree/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||||
const noteId = req.params.noteId;
|
const noteId = req.params.noteId;
|
||||||
|
|
||||||
const dataKey = protected_session.getDataKey(req);
|
res.send(await getSubTreeScripts(noteId, [noteId], req));
|
||||||
|
|
||||||
res.send(await getSubTreeScripts(noteId, [noteId], dataKey));
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
async function getSubTreeScripts(parentId, includedNoteIds, dataKey) {
|
async function getSubTreeScripts(parentId, includedNoteIds, dataKey) {
|
||||||
const childs = await sql.getAll(`SELECT notes.note_id, notes.note_title, notes.note_text, notes.is_protected
|
const children = await sql.getAll(`SELECT notes.note_id, notes.note_title, notes.note_text, notes.is_protected
|
||||||
FROM notes JOIN notes_tree USING(note_id)
|
FROM notes JOIN notes_tree USING(note_id)
|
||||||
WHERE notes_tree.is_deleted = 0 AND notes.is_deleted = 0
|
WHERE notes_tree.is_deleted = 0 AND notes.is_deleted = 0
|
||||||
AND notes_tree.parent_note_id = ? AND notes.type = 'code'
|
AND notes_tree.parent_note_id = ? AND notes.type = 'code'
|
||||||
AND (notes.mime = 'application/javascript' OR notes.mime = 'text/html')`, [parentId]);
|
AND (notes.mime = 'application/javascript' OR notes.mime = 'text/html')`, [parentId]);
|
||||||
|
|
||||||
|
protected_session.decryptNotes(dataKey, children);
|
||||||
|
|
||||||
let script = "\r\n";
|
let script = "\r\n";
|
||||||
|
|
||||||
for (const child of childs) {
|
for (const child of children) {
|
||||||
if (includedNoteIds.includes(child.note_id)) {
|
if (includedNoteIds.includes(child.note_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -45,15 +44,6 @@ async function getSubTreeScripts(parentId, includedNoteIds, dataKey) {
|
|||||||
|
|
||||||
script += await getSubTreeScripts(child.note_id, includedNoteIds, dataKey);
|
script += await getSubTreeScripts(child.note_id, includedNoteIds, dataKey);
|
||||||
|
|
||||||
if (child.is_protected) {
|
|
||||||
if (!dataKey) {
|
|
||||||
throw new Error("Protected note is included, but script isn't running in protected session.");
|
|
||||||
}
|
|
||||||
|
|
||||||
child.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(child.note_id), child.note_title);
|
|
||||||
child.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(child.note_id), child.note_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
script += child.note_text + "\r\n";
|
script += child.note_text + "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ const options = require('../../services/options');
|
|||||||
const utils = require('../../services/utils');
|
const utils = require('../../services/utils');
|
||||||
const auth = require('../../services/auth');
|
const auth = require('../../services/auth');
|
||||||
const protected_session = require('../../services/protected_session');
|
const protected_session = require('../../services/protected_session');
|
||||||
const data_encryption = require('../../services/data_encryption');
|
|
||||||
const sync_table = require('../../services/sync_table');
|
const sync_table = require('../../services/sync_table');
|
||||||
const wrap = require('express-promise-wrap').wrap;
|
const wrap = require('express-promise-wrap').wrap;
|
||||||
|
|
||||||
@ -27,13 +26,7 @@ router.get('/', auth.checkApiAuth, wrap(async (req, res, next) => {
|
|||||||
ORDER BY
|
ORDER BY
|
||||||
note_position`);
|
note_position`);
|
||||||
|
|
||||||
const dataKey = protected_session.getDataKey(req);
|
protected_session.decryptNotes(req, notes);
|
||||||
|
|
||||||
for (const note of notes) {
|
|
||||||
if (note.is_protected) {
|
|
||||||
note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
notes: notes,
|
notes: notes,
|
||||||
|
@ -5,6 +5,7 @@ const notes = require('./notes');
|
|||||||
const data_encryption = require('./data_encryption');
|
const data_encryption = require('./data_encryption');
|
||||||
const sync_table = require('./sync_table');
|
const sync_table = require('./sync_table');
|
||||||
const attributes = require('./attributes');
|
const attributes = require('./attributes');
|
||||||
|
const protected_session = require('./protected_session');
|
||||||
|
|
||||||
async function createNewNote(parentNoteId, note, sourceId) {
|
async function createNewNote(parentNoteId, note, sourceId) {
|
||||||
const noteId = utils.newNoteId();
|
const noteId = utils.newNoteId();
|
||||||
@ -67,11 +68,6 @@ async function createNewNote(parentNoteId, note, sourceId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function encryptNote(note, dataKey) {
|
|
||||||
note.detail.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.detail.note_id), note.detail.note_title);
|
|
||||||
note.detail.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(note.detail.note_id), note.detail.note_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function protectNoteRecursively(noteId, dataKey, protect, sourceId) {
|
async function protectNoteRecursively(noteId, dataKey, protect, sourceId) {
|
||||||
const note = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]);
|
const note = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]);
|
||||||
|
|
||||||
@ -84,24 +80,20 @@ async function protectNoteRecursively(noteId, dataKey, protect, sourceId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decryptNote(note, dataKey) {
|
|
||||||
note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
|
||||||
note.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text);
|
|
||||||
note.is_protected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function protectNote(note, dataKey, protect, sourceId) {
|
async function protectNote(note, dataKey, protect, sourceId) {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
if (protect && !note.is_protected) {
|
if (protect && !note.is_protected) {
|
||||||
note.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
protected_session.encryptNote(dataKey, note);
|
||||||
note.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text);
|
|
||||||
note.is_protected = true;
|
note.is_protected = true;
|
||||||
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
else if (!protect && note.is_protected) {
|
else if (!protect && note.is_protected) {
|
||||||
decryptNote(note, dataKey);
|
protected_session.decryptNote(dataKey, note);
|
||||||
|
|
||||||
|
note.is_protected = false;
|
||||||
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@ -121,13 +113,13 @@ async function protectNoteHistory(noteId, dataKey, protect, sourceId) {
|
|||||||
|
|
||||||
for (const history of historyToChange) {
|
for (const history of historyToChange) {
|
||||||
if (protect) {
|
if (protect) {
|
||||||
history.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title);
|
protected_session.encryptNoteHistoryRow(dataKey, history);
|
||||||
history.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text);
|
|
||||||
history.is_protected = true;
|
history.is_protected = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
history.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title);
|
protected_session.decryptNoteHistoryRow(dataKey, history);
|
||||||
history.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text);
|
|
||||||
history.is_protected = false;
|
history.is_protected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +134,9 @@ async function saveNoteHistory(noteId, dataKey, sourceId, nowStr) {
|
|||||||
const oldNote = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]);
|
const oldNote = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]);
|
||||||
|
|
||||||
if (oldNote.is_protected) {
|
if (oldNote.is_protected) {
|
||||||
decryptNote(oldNote, dataKey);
|
protected_session.decryptNote(dataKey, oldNote);
|
||||||
|
|
||||||
|
note.is_protected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newNoteHistoryId = utils.newNoteHistoryId();
|
const newNoteHistoryId = utils.newNoteHistoryId();
|
||||||
@ -210,11 +204,11 @@ async function saveNoteImages(noteId, noteText, sourceId) {
|
|||||||
|
|
||||||
async function updateNote(noteId, newNote, dataKey, sourceId) {
|
async function updateNote(noteId, newNote, dataKey, sourceId) {
|
||||||
if (newNote.detail.note_text === '<p> </p>') {
|
if (newNote.detail.note_text === '<p> </p>') {
|
||||||
newNote.detail.note_text = '';
|
newNote.detail.note_text = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newNote.detail.is_protected) {
|
if (newNote.detail.is_protected) {
|
||||||
await encryptNote(newNote, dataKey);
|
await protected_session.encryptNote(dataKey, newNote.detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributesMap = await attributes.getNoteAttributeMap(noteId);
|
const attributesMap = await attributes.getNoteAttributeMap(noteId);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
|
const data_encryption = require('./data_encryption');
|
||||||
const session = {};
|
const session = {};
|
||||||
|
|
||||||
function setDataKey(req, decryptedDataKey) {
|
function setDataKey(req, decryptedDataKey) {
|
||||||
@ -14,8 +15,16 @@ function getProtectedSessionId(req) {
|
|||||||
return req.headers.protected_session_id;
|
return req.headers.protected_session_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDataKey(req) {
|
/**
|
||||||
const protectedSessionId = getProtectedSessionId(req);
|
* @param obj - can be either array, in that case it's considered to be already dataKey and we just return it
|
||||||
|
* if it's not a array, we consider it a request object and try to pull dataKey based on the session id header
|
||||||
|
*/
|
||||||
|
function getDataKey(obj) {
|
||||||
|
if (!obj || obj.constructor.name === 'Array') {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
const protectedSessionId = getProtectedSessionId(obj);
|
||||||
|
|
||||||
if (protectedSessionId && session.protectedSessionId === protectedSessionId) {
|
if (protectedSessionId && session.protectedSessionId === protectedSessionId) {
|
||||||
return session.decryptedDataKey;
|
return session.decryptedDataKey;
|
||||||
@ -31,8 +40,76 @@ function isProtectedSessionAvailable(req) {
|
|||||||
return protectedSessionId && session.protectedSessionId === protectedSessionId;
|
return protectedSessionId && session.protectedSessionId === protectedSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decryptNote(dataKey, note) {
|
||||||
|
dataKey = getDataKey(dataKey);
|
||||||
|
|
||||||
|
if (!note.is_protected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.note_title) {
|
||||||
|
note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.note_text) {
|
||||||
|
note.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptNotes(dataKey, notes) {
|
||||||
|
dataKey = getDataKey(dataKey);
|
||||||
|
|
||||||
|
for (const note of notes) {
|
||||||
|
decryptNote(dataKey, note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptNoteHistoryRow(dataKey, hist) {
|
||||||
|
dataKey = getDataKey(dataKey);
|
||||||
|
|
||||||
|
if (!hist.is_protected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hist.note_title) {
|
||||||
|
hist.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(hist.note_history_id), hist.note_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hist.note_text) {
|
||||||
|
hist.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(hist.note_history_id), hist.note_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptNoteHistoryRows(dataKey, historyRows) {
|
||||||
|
dataKey = getDataKey(dataKey);
|
||||||
|
|
||||||
|
for (const hist of historyRows) {
|
||||||
|
decryptNoteHistoryRow(dataKey, hist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function encryptNote(dataKey, note) {
|
||||||
|
dataKey = getDataKey(dataKey);
|
||||||
|
|
||||||
|
note.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
||||||
|
note.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encryptNoteHistoryRow(dataKey, history) {
|
||||||
|
dataKey = getDataKey(dataKey);
|
||||||
|
|
||||||
|
history.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title);
|
||||||
|
history.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setDataKey,
|
setDataKey,
|
||||||
getDataKey,
|
getDataKey,
|
||||||
isProtectedSessionAvailable
|
isProtectedSessionAvailable,
|
||||||
|
decryptNote,
|
||||||
|
decryptNotes,
|
||||||
|
decryptNoteHistoryRow,
|
||||||
|
decryptNoteHistoryRows,
|
||||||
|
encryptNote,
|
||||||
|
encryptNoteHistoryRow
|
||||||
};
|
};
|
@ -80,17 +80,13 @@ async function loadSubTreeNoteIds(parentNoteId, subTreeNoteIds) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sortNotesAlphabetically(parentNoteId, dataKey, sourceId) {
|
async function sortNotesAlphabetically(parentNoteId, req, sourceId) {
|
||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
const notes = await sql.getAll(`SELECT note_tree_id, note_id, note_title, is_protected
|
const notes = await sql.getAll(`SELECT note_tree_id, note_id, note_title, is_protected
|
||||||
FROM notes JOIN notes_tree USING(note_id)
|
FROM notes JOIN notes_tree USING(note_id)
|
||||||
WHERE notes_tree.is_deleted = 0 AND parent_note_id = ?`, [parentNoteId]);
|
WHERE notes_tree.is_deleted = 0 AND parent_note_id = ?`, [parentNoteId]);
|
||||||
|
|
||||||
for (const note of notes) {
|
protected_session.decryptNotes(req, notes);
|
||||||
if (note.is_protected) {
|
|
||||||
note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notes.sort((a, b) => a.note_title.toLowerCase() < b.note_title.toLowerCase() ? -1 : 1);
|
notes.sort((a, b) => a.note_title.toLowerCase() < b.note_title.toLowerCase() ? -1 : 1);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user