autocomplete supports encrypted notes now as well

This commit is contained in:
azivner 2018-04-20 00:12:01 -04:00
parent 8c54b62f07
commit 4ce5ea9886
7 changed files with 100 additions and 35 deletions

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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,

View file

@ -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
View 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
};

View file

@ -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,

View file

@ -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,