mirror of
https://github.com/zadam/trilium.git
synced 2025-01-18 04:59:56 +08:00
refactoring / unification of note encryption / decryption
This commit is contained in:
parent
18709eb340
commit
74fff39c3f
7 changed files with 105 additions and 69 deletions
|
@ -4,7 +4,6 @@ const express = require('express');
|
|||
const router = express.Router();
|
||||
const sql = require('../../services/sql');
|
||||
const auth = require('../../services/auth');
|
||||
const data_encryption = require('../../services/data_encryption');
|
||||
const protected_session = require('../../services/protected_session');
|
||||
const sync_table = require('../../services/sync_table');
|
||||
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) => {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
protected_session.decryptNoteHistoryRows(req, history);
|
||||
|
||||
res.send(history);
|
||||
}));
|
||||
|
|
|
@ -24,12 +24,7 @@ router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
|||
return res.status(404).send({});
|
||||
}
|
||||
|
||||
if (detail.is_protected) {
|
||||
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);
|
||||
}
|
||||
protected_session.decryptNote(req, detail);
|
||||
|
||||
res.send({
|
||||
detail: detail
|
||||
|
|
|
@ -7,7 +7,6 @@ const wrap = require('express-promise-wrap').wrap;
|
|||
const log = require('../../services/log');
|
||||
const sql = require('../../services/sql');
|
||||
const protected_session = require('../../services/protected_session');
|
||||
const data_encryption = require('../../services/data_encryption');
|
||||
|
||||
router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||
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) => {
|
||||
const noteId = req.params.noteId;
|
||||
|
||||
const dataKey = protected_session.getDataKey(req);
|
||||
|
||||
res.send(await getSubTreeScripts(noteId, [noteId], dataKey));
|
||||
res.send(await getSubTreeScripts(noteId, [noteId], req));
|
||||
}));
|
||||
|
||||
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)
|
||||
WHERE notes_tree.is_deleted = 0 AND notes.is_deleted = 0
|
||||
AND notes_tree.parent_note_id = ? AND notes.type = 'code'
|
||||
AND (notes.mime = 'application/javascript' OR notes.mime = 'text/html')`, [parentId]);
|
||||
|
||||
protected_session.decryptNotes(dataKey, children);
|
||||
|
||||
let script = "\r\n";
|
||||
|
||||
for (const child of childs) {
|
||||
for (const child of children) {
|
||||
if (includedNoteIds.includes(child.note_id)) {
|
||||
return;
|
||||
}
|
||||
|
@ -45,15 +44,6 @@ async function getSubTreeScripts(parentId, 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";
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ const options = require('../../services/options');
|
|||
const utils = require('../../services/utils');
|
||||
const auth = require('../../services/auth');
|
||||
const protected_session = require('../../services/protected_session');
|
||||
const data_encryption = require('../../services/data_encryption');
|
||||
const sync_table = require('../../services/sync_table');
|
||||
const wrap = require('express-promise-wrap').wrap;
|
||||
|
||||
|
@ -27,13 +26,7 @@ router.get('/', auth.checkApiAuth, wrap(async (req, res, next) => {
|
|||
ORDER BY
|
||||
note_position`);
|
||||
|
||||
const dataKey = protected_session.getDataKey(req);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
protected_session.decryptNotes(req, notes);
|
||||
|
||||
res.send({
|
||||
notes: notes,
|
||||
|
|
|
@ -5,6 +5,7 @@ const notes = require('./notes');
|
|||
const data_encryption = require('./data_encryption');
|
||||
const sync_table = require('./sync_table');
|
||||
const attributes = require('./attributes');
|
||||
const protected_session = require('./protected_session');
|
||||
|
||||
async function createNewNote(parentNoteId, note, sourceId) {
|
||||
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) {
|
||||
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) {
|
||||
let changed = false;
|
||||
|
||||
if (protect && !note.is_protected) {
|
||||
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);
|
||||
protected_session.encryptNote(dataKey, note);
|
||||
|
||||
note.is_protected = true;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
else if (!protect && note.is_protected) {
|
||||
decryptNote(note, dataKey);
|
||||
protected_session.decryptNote(dataKey, note);
|
||||
|
||||
note.is_protected = false;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
@ -121,13 +113,13 @@ async function protectNoteHistory(noteId, dataKey, protect, sourceId) {
|
|||
|
||||
for (const history of historyToChange) {
|
||||
if (protect) {
|
||||
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);
|
||||
protected_session.encryptNoteHistoryRow(dataKey, history);
|
||||
|
||||
history.is_protected = true;
|
||||
}
|
||||
else {
|
||||
history.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title);
|
||||
history.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text);
|
||||
protected_session.decryptNoteHistoryRow(dataKey, history);
|
||||
|
||||
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]);
|
||||
|
||||
if (oldNote.is_protected) {
|
||||
decryptNote(oldNote, dataKey);
|
||||
protected_session.decryptNote(dataKey, oldNote);
|
||||
|
||||
note.is_protected = false;
|
||||
}
|
||||
|
||||
const newNoteHistoryId = utils.newNoteHistoryId();
|
||||
|
@ -210,11 +204,11 @@ async function saveNoteImages(noteId, noteText, sourceId) {
|
|||
|
||||
async function updateNote(noteId, newNote, dataKey, sourceId) {
|
||||
if (newNote.detail.note_text === '<p> </p>') {
|
||||
newNote.detail.note_text = '';
|
||||
newNote.detail.note_text = ''
|
||||
}
|
||||
|
||||
if (newNote.detail.is_protected) {
|
||||
await encryptNote(newNote, dataKey);
|
||||
await protected_session.encryptNote(dataKey, newNote.detail);
|
||||
}
|
||||
|
||||
const attributesMap = await attributes.getNoteAttributeMap(noteId);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
const utils = require('./utils');
|
||||
const data_encryption = require('./data_encryption');
|
||||
const session = {};
|
||||
|
||||
function setDataKey(req, decryptedDataKey) {
|
||||
|
@ -14,8 +15,16 @@ function getProtectedSessionId(req) {
|
|||
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) {
|
||||
return session.decryptedDataKey;
|
||||
|
@ -31,8 +40,76 @@ function isProtectedSessionAvailable(req) {
|
|||
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 = {
|
||||
setDataKey,
|
||||
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 () => {
|
||||
const notes = await sql.getAll(`SELECT note_tree_id, note_id, note_title, is_protected
|
||||
FROM notes JOIN notes_tree USING(note_id)
|
||||
WHERE notes_tree.is_deleted = 0 AND parent_note_id = ?`, [parentNoteId]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
protected_session.decryptNotes(req, notes);
|
||||
|
||||
notes.sort((a, b) => a.note_title.toLowerCase() < b.note_title.toLowerCase() ? -1 : 1);
|
||||
|
||||
|
|
Loading…
Reference in a new issue