mirror of
https://github.com/zadam/trilium.git
synced 2025-02-23 06:26:31 +08:00
autocomplete supports encrypted notes now as well
This commit is contained in:
parent
8c54b62f07
commit
4ce5ea9886
7 changed files with 100 additions and 35 deletions
|
@ -1,7 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const protected_session = require('../services/protected_session');
|
||||
const protectedSessionService = require('../services/protected_session');
|
||||
const repository = require('../services/repository');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
|
@ -14,7 +14,7 @@ class Note extends Entity {
|
|||
|
||||
// check if there's noteId, otherwise this is a new entity which wasn't encrypted yet
|
||||
if (this.isProtected && this.noteId) {
|
||||
protected_session.decryptNote(this);
|
||||
protectedSessionService.decryptNote(this);
|
||||
}
|
||||
|
||||
this.setContent(this.content);
|
||||
|
@ -146,7 +146,7 @@ class Note extends Entity {
|
|||
}
|
||||
|
||||
if (this.isProtected) {
|
||||
protected_session.encryptNote(this);
|
||||
protectedSessionService.encryptNote(this);
|
||||
}
|
||||
|
||||
if (!this.isDeleted) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const protected_session = require('../services/protected_session');
|
||||
const utils = require('../services/utils');
|
||||
const protectedSessionService = require('../services/protected_session');
|
||||
const repository = require('../services/repository');
|
||||
|
||||
class NoteRevision extends Entity {
|
||||
|
@ -13,7 +12,7 @@ class NoteRevision extends Entity {
|
|||
super(row);
|
||||
|
||||
if (this.isProtected) {
|
||||
protected_session.decryptNoteRevision(this);
|
||||
protectedSessionService.decryptNoteRevision(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +24,7 @@ class NoteRevision extends Entity {
|
|||
super.beforeSaving();
|
||||
|
||||
if (this.isProtected) {
|
||||
protected_session.encryptNoteRevision(this);
|
||||
protectedSessionService.encryptNoteRevision(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ const sourceIdService = require('../../services/source_id');
|
|||
const passwordEncryptionService = require('../../services/password_encryption');
|
||||
const protectedSessionService = require('../../services/protected_session');
|
||||
const appInfo = require('../../services/app_info');
|
||||
const eventService = require('../../services/events');
|
||||
const cls = require('../../services/cls');
|
||||
|
||||
async function loginSync(req) {
|
||||
const timestampStr = req.body.timestamp;
|
||||
|
@ -53,7 +55,11 @@ async function loginToProtectedSession(req) {
|
|||
|
||||
const decryptedDataKey = await passwordEncryptionService.getDataKey(password);
|
||||
|
||||
const protectedSessionId = protectedSessionService.setDataKey(req, decryptedDataKey);
|
||||
const protectedSessionId = protectedSessionService.setDataKey(decryptedDataKey);
|
||||
|
||||
cls.namespace.set('protectedSessionId', protectedSessionId);
|
||||
|
||||
eventService.emit(eventService.ENTER_PROTECTED_SESSION);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
const sql = require('./sql');
|
||||
const sqlInit = require('./sql_init');
|
||||
const syncTableService = require('./sync_table');
|
||||
const eventService = require('./events');
|
||||
const repository = require('./repository');
|
||||
const protectedSessionService = require('./protected_session');
|
||||
|
||||
let noteTitles;
|
||||
let protectedNoteTitles;
|
||||
let noteIds;
|
||||
const childToParent = {};
|
||||
const hideInAutocomplete = {};
|
||||
|
@ -15,7 +17,7 @@ async function load() {
|
|||
noteTitles = await sql.getMap(`SELECT noteId, LOWER(title) FROM notes WHERE isDeleted = 0 AND isProtected = 0`);
|
||||
noteIds = Object.keys(noteTitles);
|
||||
|
||||
prefixes = await sql.getMap(`SELECT noteId || '-' || parentNoteId, prefix FROM branches WHERE prefix IS NOT NULL AND prefix != ''`);
|
||||
prefixes = await sql.getMap(`SELECT noteId || '-' || parentNoteId, LOWER(prefix) FROM branches WHERE prefix IS NOT NULL AND prefix != ''`);
|
||||
|
||||
const relations = await sql.getRows(`SELECT noteId, parentNoteId FROM branches WHERE isDeleted = 0`);
|
||||
|
||||
|
@ -39,7 +41,13 @@ function getResults(query) {
|
|||
const tokens = query.toLowerCase().split(" ");
|
||||
const results = [];
|
||||
|
||||
for (const noteId in noteTitles) {
|
||||
let noteIds = Object.keys(noteTitles);
|
||||
|
||||
if (protectedSessionService.isProtectedSessionAvailable()) {
|
||||
noteIds = noteIds.concat(Object.keys(protectedNoteTitles));
|
||||
}
|
||||
|
||||
for (const noteId of noteIds) {
|
||||
if (hideInAutocomplete[noteId]) {
|
||||
continue;
|
||||
}
|
||||
|
@ -50,8 +58,7 @@ function getResults(query) {
|
|||
}
|
||||
|
||||
for (const parentNoteId of parents) {
|
||||
const prefix = prefixes[noteId + '-' + parentNoteId];
|
||||
const title = (prefix || '') + ' ' + noteTitles[noteId];
|
||||
const title = getNoteTitle(noteId, parentNoteId);
|
||||
const foundTokens = [];
|
||||
|
||||
for (const token of tokens) {
|
||||
|
@ -78,7 +85,7 @@ function search(noteId, tokens, path, results) {
|
|||
const retPath = getSomePath(noteId, path);
|
||||
|
||||
if (retPath) {
|
||||
const noteTitle = getNoteTitle(retPath);
|
||||
const noteTitle = getNoteTitleForPath(retPath);
|
||||
|
||||
results.push({
|
||||
title: noteTitle,
|
||||
|
@ -102,9 +109,7 @@ function search(noteId, tokens, path, results) {
|
|||
if (parentNoteId === 'root' || hideInAutocomplete[parentNoteId]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const prefix = prefixes[noteId + '-' + parentNoteId];
|
||||
const title = (prefix || '') + ' ' + noteTitles[noteId];
|
||||
const title = getNoteTitle(noteId, parentNoteId);
|
||||
const foundTokens = [];
|
||||
|
||||
for (const token of tokens) {
|
||||
|
@ -124,14 +129,30 @@ function search(noteId, tokens, path, results) {
|
|||
}
|
||||
}
|
||||
|
||||
function getNoteTitle(path) {
|
||||
function getNoteTitle(noteId, parentNoteId) {
|
||||
const prefix = prefixes[noteId + '-' + parentNoteId];
|
||||
|
||||
let title = noteTitles[noteId];
|
||||
|
||||
if (!title) {
|
||||
if (protectedSessionService.isProtectedSessionAvailable()) {
|
||||
title = protectedNoteTitles[noteId];
|
||||
}
|
||||
else {
|
||||
title = '[protected]';
|
||||
}
|
||||
}
|
||||
|
||||
return (prefix ? (prefix + ' - ') : '') + title;
|
||||
}
|
||||
|
||||
function getNoteTitleForPath(path) {
|
||||
const titles = [];
|
||||
|
||||
let parentNoteId = 'root';
|
||||
|
||||
for (const noteId of path) {
|
||||
const prefix = prefixes[noteId + '-' + parentNoteId];
|
||||
const title = (prefix ? (prefix + ' - ') : '') + noteTitles[noteId];
|
||||
const title = getNoteTitle(noteId, parentNoteId);
|
||||
|
||||
titles.push(title);
|
||||
parentNoteId = noteId;
|
||||
|
@ -163,7 +184,7 @@ function getSomePath(noteId, path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
syncTableService.addListener(async (entityName, entityId) => {
|
||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => {
|
||||
if (entityName === 'notes') {
|
||||
const note = await repository.getNote(entityId);
|
||||
|
||||
|
@ -212,6 +233,14 @@ syncTableService.addListener(async (entityName, entityId) => {
|
|||
}
|
||||
});
|
||||
|
||||
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, async () => {
|
||||
protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`);
|
||||
|
||||
for (const noteId in protectedNoteTitles) {
|
||||
protectedNoteTitles[noteId] = protectedSessionService.decryptNoteTitle(noteId, protectedNoteTitles[noteId]);
|
||||
}
|
||||
});
|
||||
|
||||
sqlInit.dbReady.then(load);
|
||||
|
||||
module.exports = {
|
||||
|
|
28
src/services/events.js
Normal file
28
src/services/events.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION";
|
||||
const ENTITY_CHANGED = "ENTITY_CHANGED";
|
||||
|
||||
const eventListeners = {};
|
||||
|
||||
function subscribe(eventType, listener) {
|
||||
eventListeners[eventType] = eventListeners[eventType] || [];
|
||||
eventListeners[eventType].push(listener);
|
||||
}
|
||||
|
||||
function emit(eventType, data) {
|
||||
const listeners = eventListeners[eventType];
|
||||
|
||||
if (listeners) {
|
||||
for (const listener of listeners) {
|
||||
// not awaiting for async processing
|
||||
listener(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
subscribe,
|
||||
emit,
|
||||
// event types:
|
||||
ENTER_PROTECTED_SESSION,
|
||||
ENTITY_CHANGED
|
||||
};
|
|
@ -6,7 +6,7 @@ const cls = require('./cls');
|
|||
|
||||
const dataKeyMap = {};
|
||||
|
||||
function setDataKey(req, decryptedDataKey) {
|
||||
function setDataKey(decryptedDataKey) {
|
||||
const protectedSessionId = utils.randomSecureToken(32);
|
||||
|
||||
dataKeyMap[protectedSessionId] = Array.from(decryptedDataKey); // can't store buffer in session
|
||||
|
@ -28,12 +28,20 @@ function getDataKey() {
|
|||
return dataKeyMap[protectedSessionId];
|
||||
}
|
||||
|
||||
function isProtectedSessionAvailable(req) {
|
||||
const protectedSessionId = getProtectedSessionId(req);
|
||||
function isProtectedSessionAvailable() {
|
||||
const protectedSessionId = getProtectedSessionId();
|
||||
|
||||
return !!dataKeyMap[protectedSessionId];
|
||||
}
|
||||
|
||||
function decryptNoteTitle(noteId, encryptedTitle) {
|
||||
const dataKey = getDataKey();
|
||||
|
||||
const iv = dataEncryptionService.noteTitleIv(noteId);
|
||||
|
||||
return dataEncryptionService.decryptString(dataKey, iv, encryptedTitle);
|
||||
}
|
||||
|
||||
function decryptNote(note) {
|
||||
const dataKey = getDataKey();
|
||||
|
||||
|
@ -99,6 +107,7 @@ module.exports = {
|
|||
setDataKey,
|
||||
getDataKey,
|
||||
isProtectedSessionAvailable,
|
||||
decryptNoteTitle,
|
||||
decryptNote,
|
||||
decryptNotes,
|
||||
decryptNoteRevision,
|
||||
|
|
|
@ -4,12 +4,7 @@ const dateUtils = require('./date_utils');
|
|||
const syncSetup = require('./sync_setup');
|
||||
const log = require('./log');
|
||||
const cls = require('./cls');
|
||||
|
||||
const listeners = [];
|
||||
|
||||
function addListener(listener) {
|
||||
listeners.push(listener);
|
||||
}
|
||||
const eventService = require('./events');
|
||||
|
||||
async function addNoteSync(noteId, sourceId) {
|
||||
await addEntitySync("notes", noteId, sourceId)
|
||||
|
@ -65,10 +60,10 @@ async function addEntitySync(entityName, entityId, sourceId) {
|
|||
await sql.execute("UPDATE options SET value = (SELECT MAX(id) FROM sync) WHERE name IN('lastSyncedPush', 'lastSyncedPull')");
|
||||
}
|
||||
|
||||
for (const listener of listeners) {
|
||||
// not awaiting to not block the execution
|
||||
listener(entityName, entityId);
|
||||
}
|
||||
eventService.emit(eventService.ENTITY_CHANGED, {
|
||||
entityName,
|
||||
entityId
|
||||
});
|
||||
}
|
||||
|
||||
async function cleanupSyncRowsForMissingEntities(entityName, entityKey) {
|
||||
|
@ -115,7 +110,6 @@ async function fillAllSyncRows() {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
addListener,
|
||||
addNoteSync,
|
||||
addBranchSync,
|
||||
addNoteReorderingSync,
|
||||
|
|
Loading…
Reference in a new issue