removed dataKey where it's not necessary anymore (use of CLS instead)

This commit is contained in:
azivner 2018-03-31 08:53:52 -04:00
parent 5d203b2278
commit 05676f3459
12 changed files with 78 additions and 105 deletions

View File

@ -11,7 +11,7 @@ class Note extends Entity {
super(repository, row); super(repository, row);
if (this.isProtected) { if (this.isProtected) {
protected_session.decryptNote(this.dataKey, this); protected_session.decryptNote(this);
} }
if (this.isJson()) { if (this.isJson()) {
@ -129,7 +129,7 @@ class Note extends Entity {
this.content = JSON.stringify(this.jsonContent, null, '\t'); this.content = JSON.stringify(this.jsonContent, null, '\t');
if (this.isProtected) { if (this.isProtected) {
protected_session.encryptNote(this.dataKey, this); protected_session.encryptNote(this);
} }
} }
} }

View File

@ -37,21 +37,18 @@ async function uploadFile(req) {
async function downloadFile(req, res) { async function downloadFile(req, res) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]);
const protectedSessionId = req.query.protectedSessionId;
if (!note) { if (!note) {
return res.status(404).send(`Note ${noteId} doesn't exist.`); return res.status(404).send(`Note ${noteId} doesn't exist.`);
} }
if (note.isProtected) { if (note.isProtected) {
const dataKey = protected_session.getDataKeyForProtectedSessionId(protectedSessionId); if (!protected_session.isProtectedSessionAvailable()) {
if (!dataKey) {
res.status(401).send("Protected session not available"); res.status(401).send("Protected session not available");
return; return;
} }
protected_session.decryptNote(dataKey, note); protected_session.decryptNote(note);
} }
const labelMap = await labels.getNoteLabelMap(noteId); const labelMap = await labels.getNoteLabelMap(noteId);

View File

@ -6,7 +6,7 @@ const protected_session = require('../../services/protected_session');
async function getNoteRevisions(req) { async function getNoteRevisions(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const revisions = await sql.getRows("SELECT * FROM note_revisions WHERE noteId = ? order by dateModifiedTo desc", [noteId]); const revisions = await sql.getRows("SELECT * FROM note_revisions WHERE noteId = ? order by dateModifiedTo desc", [noteId]);
protected_session.decryptNoteRevisions(req, revisions); protected_session.decryptNoteRevisions(revisions);
return revisions; return revisions;
} }

View File

@ -20,7 +20,7 @@ async function getNote(req) {
return [404, "Note " + noteId + " has not been found."]; return [404, "Note " + noteId + " has not been found."];
} }
protected_session.decryptNote(req, note); protected_session.decryptNote(note);
if (note.type === 'file') { if (note.type === 'file') {
// no need to transfer (potentially large) file payload for this request // no need to transfer (potentially large) file payload for this request
@ -46,24 +46,21 @@ async function createNote(req) {
async function updateNote(req) { async function updateNote(req) {
const note = req.body; const note = req.body;
const noteId = req.params.noteId; const noteId = req.params.noteId;
const dataKey = protected_session.getDataKey(req);
await notes.updateNote(noteId, note, dataKey); await notes.updateNote(noteId, note);
} }
async function sortNotes(req) { async function sortNotes(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const dataKey = protected_session.getDataKey(req);
await tree.sortNotesAlphabetically(noteId, dataKey); await tree.sortNotesAlphabetically(noteId);
} }
async function protectBranch(req) { async function protectBranch(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const isProtected = !!parseInt(req.params.isProtected); const isProtected = !!parseInt(req.params.isProtected);
const dataKey = protected_session.getDataKey(req);
await notes.protectNoteRecursively(noteId, dataKey, isProtected); await notes.protectNoteRecursively(noteId, isProtected);
} }
async function setNoteTypeMime(req) { async function setNoteTypeMime(req) {

View File

@ -45,7 +45,7 @@ async function getTree(req) {
WHERE WHERE
notes.isDeleted = 0`)); notes.isDeleted = 0`));
protected_session.decryptNotes(req, notes); protected_session.decryptNotes(notes);
notes.forEach(note => { notes.forEach(note => {
note.hideInAutocomplete = !!note.hideInAutocomplete; note.hideInAutocomplete = !!note.hideInAutocomplete;

View File

@ -38,6 +38,7 @@ const router = express.Router();
const auth = require('../services/auth'); const auth = require('../services/auth');
const cls = require('../services/cls'); const cls = require('../services/cls');
const sql = require('../services/sql'); const sql = require('../services/sql');
const protectedSessionService = require('../services/protected_session');
function apiResultHandler(res, result) { function apiResultHandler(res, result) {
// if it's an array and first element is integer then we consider this to be [statusCode, response] format // if it's an array and first element is integer then we consider this to be [statusCode, response] format
@ -67,6 +68,7 @@ function route(method, path, middleware, routeHandler, resultHandler) {
try { try {
const result = await cls.init(async () => { const result = await cls.init(async () => {
cls.namespace.set('sourceId', req.headers.source_id); cls.namespace.set('sourceId', req.headers.source_id);
protectedSessionService.setProtectedSessionId(req);
return await sql.doInTransaction(async () => { return await sql.doInTransaction(async () => {
return await routeHandler(req, res, next); return await routeHandler(req, res, next);

View File

@ -5,7 +5,7 @@ const sync_table = require('./sync_table');
const labels = require('./labels'); const labels = require('./labels');
const protected_session = require('./protected_session'); const protected_session = require('./protected_session');
async function createNewNote(parentNoteId, noteOpts, dataKey) { async function createNewNote(parentNoteId, noteOpts) {
const noteId = utils.newNoteId(); const noteId = utils.newNoteId();
const branchId = utils.newBranchId(); const branchId = utils.newBranchId();
@ -57,7 +57,7 @@ async function createNewNote(parentNoteId, noteOpts, dataKey) {
}; };
if (note.isProtected) { if (note.isProtected) {
protected_session.encryptNote(dataKey, note); protected_session.encryptNote(note);
} }
await sql.insert("notes", note); await sql.insert("notes", note);
@ -106,7 +106,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
note.mime = "text/html"; note.mime = "text/html";
} }
const {noteId} = await createNewNote(parentNoteId, note, extraOptions.dataKey); const {noteId} = await createNewNote(parentNoteId, note);
if (extraOptions.labels) { if (extraOptions.labels) {
for (const attrName in extraOptions.labels) { for (const attrName in extraOptions.labels) {
@ -117,30 +117,30 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
return noteId; return noteId;
} }
async function protectNoteRecursively(noteId, dataKey, protect) { async function protectNoteRecursively(noteId, protect) {
const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]);
await protectNote(note, dataKey, protect); await protectNote(note, protect);
const children = await sql.getColumn("SELECT noteId FROM branches WHERE parentNoteId = ? AND isDeleted = 0", [noteId]); const children = await sql.getColumn("SELECT noteId FROM branches WHERE parentNoteId = ? AND isDeleted = 0", [noteId]);
for (const childNoteId of children) { for (const childNoteId of children) {
await protectNoteRecursively(childNoteId, dataKey, protect); await protectNoteRecursively(childNoteId, protect);
} }
} }
async function protectNote(note, dataKey, protect) { async function protectNote(note, protect) {
let changed = false; let changed = false;
if (protect && !note.isProtected) { if (protect && !note.isProtected) {
protected_session.encryptNote(dataKey, note); protected_session.encryptNote(note);
note.isProtected = true; note.isProtected = true;
changed = true; changed = true;
} }
else if (!protect && note.isProtected) { else if (!protect && note.isProtected) {
protected_session.decryptNote(dataKey, note); protected_session.decryptNote(note);
note.isProtected = false; note.isProtected = false;
@ -154,20 +154,20 @@ async function protectNote(note, dataKey, protect) {
await sync_table.addNoteSync(note.noteId); await sync_table.addNoteSync(note.noteId);
} }
await protectNoteRevisions(note.noteId, dataKey, protect); await protectNoteRevisions(note.noteId, protect);
} }
async function protectNoteRevisions(noteId, dataKey, protect) { async function protectNoteRevisions(noteId, protect) {
const revisionsToChange = await sql.getRows("SELECT * FROM note_revisions WHERE noteId = ? AND isProtected != ?", [noteId, protect]); const revisionsToChange = await sql.getRows("SELECT * FROM note_revisions WHERE noteId = ? AND isProtected != ?", [noteId, protect]);
for (const revision of revisionsToChange) { for (const revision of revisionsToChange) {
if (protect) { if (protect) {
protected_session.encryptNoteRevision(dataKey, revision); protected_session.encryptNoteRevision(revision);
revision.isProtected = true; revision.isProtected = true;
} }
else { else {
protected_session.decryptNoteRevision(dataKey, revision); protected_session.decryptNoteRevision(revision);
revision.isProtected = false; revision.isProtected = false;
} }
@ -179,7 +179,7 @@ async function protectNoteRevisions(noteId, dataKey, protect) {
} }
} }
async function saveNoteRevision(noteId, dataKey, nowStr) { async function saveNoteRevision(noteId, nowStr) {
const oldNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); const oldNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]);
if (oldNote.type === 'file') { if (oldNote.type === 'file') {
@ -187,7 +187,7 @@ async function saveNoteRevision(noteId, dataKey, nowStr) {
} }
if (oldNote.isProtected) { if (oldNote.isProtected) {
protected_session.decryptNote(dataKey, oldNote); protected_session.decryptNote(oldNote);
oldNote.isProtected = false; oldNote.isProtected = false;
} }
@ -255,23 +255,23 @@ async function saveNoteImages(noteId, noteText) {
} }
} }
async function loadFile(noteId, newNote, dataKey) { async function loadFile(noteId, newNote) {
const oldNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); const oldNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]);
if (oldNote.isProtected) { if (oldNote.isProtected) {
await protected_session.decryptNote(dataKey, oldNote); await protected_session.decryptNote(oldNote);
} }
newNote.content = oldNote.content; newNote.content = oldNote.content;
} }
async function updateNote(noteId, newNote, dataKey) { async function updateNote(noteId, newNote) {
if (newNote.type === 'file') { if (newNote.type === 'file') {
await loadFile(noteId, newNote, dataKey); await loadFile(noteId, newNote);
} }
if (newNote.isProtected) { if (newNote.isProtected) {
await protected_session.encryptNote(dataKey, newNote); await protected_session.encryptNote(newNote);
} }
const labelsMap = await labels.getNoteLabelMap(noteId); const labelsMap = await labels.getNoteLabelMap(noteId);
@ -293,12 +293,12 @@ async function updateNote(noteId, newNote, dataKey) {
&& !existingnoteRevisionId && !existingnoteRevisionId
&& msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
await saveNoteRevision(noteId, dataKey, nowStr); await saveNoteRevision(noteId, nowStr);
} }
await saveNoteImages(noteId, newNote.content); await saveNoteImages(noteId, newNote.content);
await protectNoteRevisions(noteId, dataKey, newNote.isProtected); await protectNoteRevisions(noteId, newNote.isProtected);
await sql.execute("UPDATE notes SET title = ?, content = ?, isProtected = ?, dateModified = ? WHERE noteId = ?", [ await sql.execute("UPDATE notes SET title = ?, content = ?, isProtected = ?, dateModified = ? WHERE noteId = ?", [
newNote.title, newNote.title,

View File

@ -2,50 +2,43 @@
const utils = require('./utils'); const utils = require('./utils');
const data_encryption = require('./data_encryption'); const data_encryption = require('./data_encryption');
const session = {}; const dataKeyMap = {};
const cls = require('./cls');
function setDataKey(req, decryptedDataKey) { function setDataKey(req, decryptedDataKey) {
session.decryptedDataKey = Array.from(decryptedDataKey); // can't store buffer in session const protectedSessionId = utils.randomSecureToken(32);
session.protectedSessionId = utils.randomSecureToken(32);
return session.protectedSessionId; dataKeyMap[protectedSessionId] = Array.from(decryptedDataKey); // can't store buffer in session
return protectedSessionId;
} }
function getProtectedSessionId(req) { function setProtectedSessionId(req) {
return req.headers.protected_session_id; cls.namespace.set('protectedSessionId', req.headers.protected_session_id);
} }
/** function getProtectedSessionId() {
* @param obj - can be either array, in that case it's considered to be already dataKey and we just return it return cls.namespace.get('protectedSessionId');
* 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); function getDataKey() {
const protectedSessionId = getProtectedSessionId();
return getDataKeyForProtectedSessionId(protectedSessionId); return dataKeyMap[protectedSessionId];
} }
function getDataKeyForProtectedSessionId(protectedSessionId) { function getDataKeyForProtectedSessionId(protectedSessionId) {
if (protectedSessionId && session.protectedSessionId === protectedSessionId) { return dataKeyMap[protectedSessionId];
return session.decryptedDataKey;
}
else {
return null;
}
} }
function isProtectedSessionAvailable(req) { function isProtectedSessionAvailable(req) {
const protectedSessionId = getProtectedSessionId(req); const protectedSessionId = getProtectedSessionId(req);
return protectedSessionId && session.protectedSessionId === protectedSessionId; return !!dataKeyMap[protectedSessionId];
} }
function decryptNote(dataKey, note) { function decryptNote(note) {
dataKey = getDataKey(dataKey); const dataKey = getDataKey();
if (!note.isProtected) { if (!note.isProtected) {
return; return;
@ -67,16 +60,16 @@ function decryptNote(dataKey, note) {
} }
} }
function decryptNotes(dataKey, notes) { function decryptNotes(notes) {
dataKey = getDataKey(dataKey); const dataKey = getDataKey();
for (const note of notes) { for (const note of notes) {
decryptNote(dataKey, note); decryptNote(note);
} }
} }
function decryptNoteRevision(dataKey, hist) { function decryptNoteRevision(hist) {
dataKey = getDataKey(dataKey); const dataKey = getDataKey();
if (!hist.isProtected) { if (!hist.isProtected) {
return; return;
@ -91,23 +84,21 @@ function decryptNoteRevision(dataKey, hist) {
} }
} }
function decryptNoteRevisions(dataKey, noteRevisions) { function decryptNoteRevisions(noteRevisions) {
dataKey = getDataKey(dataKey);
for (const revision of noteRevisions) { for (const revision of noteRevisions) {
decryptNoteRevision(dataKey, revision); decryptNoteRevision(revision);
} }
} }
function encryptNote(dataKey, note) { function encryptNote(note) {
dataKey = getDataKey(dataKey); const dataKey = getDataKey();
note.title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.noteId), note.title); note.title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.noteId), note.title);
note.content = data_encryption.encrypt(dataKey, data_encryption.noteContentIv(note.noteId), note.content); note.content = data_encryption.encrypt(dataKey, data_encryption.noteContentIv(note.noteId), note.content);
} }
function encryptNoteRevision(dataKey, revision) { function encryptNoteRevision(revision) {
dataKey = getDataKey(dataKey); const dataKey = getDataKey();
revision.title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(revision.noteRevisionId), revision.title); revision.title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(revision.noteRevisionId), revision.title);
revision.content = data_encryption.encrypt(dataKey, data_encryption.noteContentIv(revision.noteRevisionId), revision.content); revision.content = data_encryption.encrypt(dataKey, data_encryption.noteContentIv(revision.noteRevisionId), revision.content);
@ -123,5 +114,6 @@ module.exports = {
decryptNoteRevision, decryptNoteRevision,
decryptNoteRevisions, decryptNoteRevisions,
encryptNote, encryptNote,
encryptNoteRevision encryptNoteRevision,
setProtectedSessionId
}; };

View File

@ -7,17 +7,9 @@ const Label = require('../entities/label');
const sync_table = require('../services/sync_table'); const sync_table = require('../services/sync_table');
class Repository { class Repository {
constructor(dataKey) {
this.dataKey = protected_session.getDataKey(dataKey);
}
async getEntities(query, params = []) { async getEntities(query, params = []) {
const rows = await sql.getRows(query, params); const rows = await sql.getRows(query, params);
for (const row of rows) {
row.dataKey = this.dataKey;
}
return rows.map(row => this.createEntityFromRow(row)); return rows.map(row => this.createEntityFromRow(row));
} }
@ -28,8 +20,6 @@ class Repository {
return null; return null;
} }
row.dataKey = this.dataKey;
return this.createEntityFromRow(row); return this.createEntityFromRow(row);
} }
@ -66,7 +56,6 @@ class Repository {
const clone = Object.assign({}, entity); const clone = Object.assign({}, entity);
delete clone.dataKey;
delete clone.jsonContent; delete clone.jsonContent;
delete clone.repository; delete clone.repository;

View File

@ -2,17 +2,17 @@ const sql = require('./sql');
const ScriptContext = require('./script_context'); const ScriptContext = require('./script_context');
const Repository = require('./repository'); const Repository = require('./repository');
async function executeNote(dataKey, note) { async function executeNote(note) {
if (!note.isJavaScript()) { if (!note.isJavaScript()) {
return; return;
} }
const bundle = await getScriptBundle(note); const bundle = await getScriptBundle(note);
await executeBundle(dataKey, bundle); await executeBundle(bundle);
} }
async function executeBundle(dataKey, bundle, startNote) { async function executeBundle(bundle, startNote) {
if (!startNote) { if (!startNote) {
// this is the default case, the only exception is when we want to preserve frontend startNote // this is the default case, the only exception is when we want to preserve frontend startNote
startNote = bundle.note; startNote = bundle.note;
@ -21,7 +21,7 @@ async function executeBundle(dataKey, bundle, startNote) {
// last \r\n is necessary if script contains line comment on its last line // last \r\n is necessary if script contains line comment on its last line
const script = "async function() {\r\n" + bundle.script + "\r\n}"; const script = "async function() {\r\n" + bundle.script + "\r\n}";
const ctx = new ScriptContext(dataKey, startNote, bundle.allNotes); const ctx = new ScriptContext(startNote, bundle.allNotes);
if (await bundle.note.hasLabel('manual_transaction_handling')) { if (await bundle.note.hasLabel('manual_transaction_handling')) {
return await execute(ctx, script, ''); return await execute(ctx, script, '');
@ -35,8 +35,8 @@ async function executeBundle(dataKey, bundle, startNote) {
* This method preserves frontend startNode - that's why we start execution from currentNote and override * This method preserves frontend startNode - that's why we start execution from currentNote and override
* bundle's startNote. * bundle's startNote.
*/ */
async function executeScript(dataKey, script, params, startNoteId, currentNoteId) { async function executeScript(script, params, startNoteId, currentNoteId) {
const repository = new Repository(dataKey); const repository = new Repository();
const startNote = await repository.getNote(startNoteId); const startNote = await repository.getNote(startNoteId);
const currentNote = await repository.getNote(currentNoteId); const currentNote = await repository.getNote(currentNoteId);
@ -46,7 +46,7 @@ async function executeScript(dataKey, script, params, startNoteId, currentNoteId
const bundle = await getScriptBundle(currentNote); const bundle = await getScriptBundle(currentNote);
return await executeBundle(dataKey, bundle, startNote); return await executeBundle(bundle, startNote);
} }
async function execute(ctx, script, paramsStr) { async function execute(ctx, script, paramsStr) {

View File

@ -9,12 +9,10 @@ const config = require('./config');
const Repository = require('./repository'); const Repository = require('./repository');
const axios = require('axios'); const axios = require('axios');
function ScriptContext(dataKey, startNote, allNotes) { function ScriptContext(startNote, allNotes) {
dataKey = protected_session.getDataKey(dataKey);
this.modules = {}; this.modules = {};
this.notes = utils.toObject(allNotes, note => [note.noteId, note]); this.notes = utils.toObject(allNotes, note => [note.noteId, note]);
this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(dataKey, startNote, note)]); this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note)]);
this.require = moduleNoteIds => { this.require = moduleNoteIds => {
return moduleName => { return moduleName => {
const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId));
@ -29,8 +27,8 @@ function ScriptContext(dataKey, startNote, allNotes) {
}; };
} }
function ScriptApi(dataKey, startNote, currentNote) { function ScriptApi(startNote, currentNote) {
const repository = new Repository(dataKey); const repository = new Repository();
this.startNote = startNote; this.startNote = startNote;
this.currentNote = currentNote; this.currentNote = currentNote;
@ -59,8 +57,6 @@ function ScriptApi(dataKey, startNote, currentNote) {
}; };
this.createNote = async function(parentNoteId, title, content = "", extraOptions = {}) { this.createNote = async function(parentNoteId, title, content = "", extraOptions = {}) {
extraOptions.dataKey = dataKey;
return await notes.createNote(parentNoteId, title, content, extraOptions); return await notes.createNote(parentNoteId, title, content, extraOptions);
}; };

View File

@ -76,13 +76,13 @@ async function loadSubTreeNoteIds(parentNoteId, subTreeNoteIds) {
} }
} }
async function sortNotesAlphabetically(parentNoteId, req) { async function sortNotesAlphabetically(parentNoteId) {
await sql.doInTransaction(async () => { await sql.doInTransaction(async () => {
const notes = await sql.getRows(`SELECT branchId, noteId, title, isProtected const notes = await sql.getRows(`SELECT branchId, noteId, title, isProtected
FROM notes JOIN branches USING(noteId) FROM notes JOIN branches USING(noteId)
WHERE branches.isDeleted = 0 AND parentNoteId = ?`, [parentNoteId]); WHERE branches.isDeleted = 0 AND parentNoteId = ?`, [parentNoteId]);
protected_session.decryptNotes(req, notes); protected_session.decryptNotes(notes);
notes.sort((a, b) => a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1); notes.sort((a, b) => a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1);