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