mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	server side WIP - saving encrypted note now works, changing terminology of "encrypted note" to "protected note"
This commit is contained in:
		
							
								
								
									
										52
									
								
								migrations/0028__rename_encryption_to_protected.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								migrations/0028__rename_encryption_to_protected.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| UPDATE audit_log SET category = 'PROTECTED' WHERE category = 'ENCRYPTION'; | ||||
|  | ||||
| DELETE FROM notes WHERE note_clone_id IS NOT NULL AND note_clone_id != ''; | ||||
|  | ||||
| CREATE TABLE `notes_mig` ( | ||||
| 	`note_id`	TEXT NOT NULL, | ||||
| 	`note_title`	TEXT, | ||||
| 	`note_text`	TEXT, | ||||
| 	`date_created`	INT, | ||||
| 	`date_modified`	INT, | ||||
| 	`is_protected`	INT NOT NULL DEFAULT 0, | ||||
| 	`is_deleted`	INT NOT NULL DEFAULT 0, | ||||
| 	PRIMARY KEY(`note_id`) | ||||
| ); | ||||
|  | ||||
| INSERT INTO notes_mig (note_id, note_title, note_text, date_created, date_modified, is_protected, is_deleted) | ||||
|     SELECT note_id, note_title, note_text, date_created, date_modified, encryption, is_deleted FROM notes; | ||||
|  | ||||
| DROP TABLE notes; | ||||
| ALTER TABLE notes_mig RENAME TO notes; | ||||
|  | ||||
| CREATE INDEX `IDX_notes_is_deleted` ON `notes` ( | ||||
| 	`is_deleted` | ||||
| ); | ||||
|  | ||||
| CREATE TABLE `notes_history_mig` ( | ||||
| 	`note_history_id`	TEXT NOT NULL PRIMARY KEY, | ||||
| 	`note_id`	TEXT NOT NULL, | ||||
| 	`note_title`	TEXT, | ||||
| 	`note_text`	TEXT, | ||||
| 	`is_protected`	INT, | ||||
| 	`date_modified_from` INT, | ||||
| 	`date_modified_to` INT | ||||
| ); | ||||
|  | ||||
| INSERT INTO notes_history_mig (note_history_id, note_id, note_title, note_text, is_protected, date_modified_from, date_modified_to) | ||||
|                         SELECT note_history_id, note_id, note_title, note_text, encryption, date_modified_from, date_modified_to FROM notes_history; | ||||
|  | ||||
| DROP TABLE notes_history; | ||||
| ALTER TABLE notes_history_mig RENAME TO notes_history; | ||||
|  | ||||
| CREATE INDEX `IDX_notes_history_note_id` ON `notes_history` ( | ||||
| 	`note_id` | ||||
| ); | ||||
|  | ||||
| CREATE INDEX `IDX_notes_history_note_date_modified_from` ON `notes_history` ( | ||||
| 	`date_modified_from` | ||||
| ); | ||||
|  | ||||
| CREATE INDEX `IDX_notes_history_note_date_modified_to` ON `notes_history` ( | ||||
| 	`date_modified_to` | ||||
| ); | ||||
| @@ -57,9 +57,9 @@ const contextMenu = (function() { | ||||
|  | ||||
|             if (ui.cmd === "insertNoteHere") { | ||||
|                 const parentKey = treeUtils.getParentKey(node); | ||||
|                 const encryption = treeUtils.getParentEncryption(node); | ||||
|                 const isProtected = treeUtils.getParentEncryption(node); | ||||
|  | ||||
|                 noteEditor.createNote(node, parentKey, 'after', encryption); | ||||
|                 noteEditor.createNote(node, parentKey, 'after', isProtected); | ||||
|             } | ||||
|             else if (ui.cmd === "insertChildNote") { | ||||
|                 noteEditor.createNote(node, node.key, 'into'); | ||||
|   | ||||
| @@ -58,7 +58,7 @@ const noteHistory = (function() { | ||||
|         let noteTitle = historyItem.note_title; | ||||
|         let noteText = historyItem.note_text; | ||||
|  | ||||
|         if (historyItem.encryption > 0) { | ||||
|         if (historyItem.is_protected) { | ||||
|             noteTitle = encryption.decryptString(noteTitle); | ||||
|             noteText = encryption.decryptString(noteText); | ||||
|         } | ||||
|   | ||||
| @@ -27,7 +27,7 @@ const encryption = (function() { | ||||
|         encryptionSessionTimeout = encSessTimeout; | ||||
|     } | ||||
|  | ||||
|     function ensureEncryptionIsAvailable(requireEncryption, modal) { | ||||
|     function ensureProtectedSession(requireEncryption, modal) { | ||||
|         const dfd = $.Deferred(); | ||||
|  | ||||
|         if (requireEncryption && !isEncryptionAvailable()) { | ||||
| @@ -116,15 +116,6 @@ const encryption = (function() { | ||||
|         return new aesjs.ModeOfOperation.ctr(dataKey, new aesjs.Counter(5)); | ||||
|     } | ||||
|  | ||||
|     function encryptNoteIfNecessary(note) { | ||||
|         if (note.detail.encryption === 0) { | ||||
|             return note; | ||||
|         } | ||||
|         else { | ||||
|             return encryptNote(note); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function encryptString(str) { | ||||
|         return encrypt(getDataAes(), str); | ||||
|     } | ||||
| @@ -184,36 +175,34 @@ const encryption = (function() { | ||||
|         note.detail.note_title = encryptString(note.detail.note_title); | ||||
|         note.detail.note_text = encryptString(note.detail.note_text); | ||||
|  | ||||
|         note.detail.encryption = 1; | ||||
|         note.detail.is_protected = true; | ||||
|  | ||||
|         return note; | ||||
|     } | ||||
|  | ||||
|     async function encryptNoteAndSendToServer() { | ||||
|         await ensureEncryptionIsAvailable(true, true); | ||||
|     async function protectNoteAndSendToServer() { | ||||
|         await ensureProtectedSession(true, true); | ||||
|  | ||||
|         const note = noteEditor.getCurrentNote(); | ||||
|  | ||||
|         noteEditor.updateNoteFromInputs(note); | ||||
|  | ||||
|         encryptNote(note); | ||||
|         note.detail.is_protected = true; | ||||
|  | ||||
|         await noteEditor.saveNoteToServer(note); | ||||
|  | ||||
|         await changeEncryptionOnNoteHistory(note.detail.note_id, true); | ||||
|  | ||||
|         noteEditor.setNoteBackgroundIfEncrypted(note); | ||||
|     } | ||||
|  | ||||
|     async function changeEncryptionOnNoteHistory(noteId, encrypt) { | ||||
|     async function changeEncryptionOnNoteHistory(noteId, protect) { | ||||
|         const result = await $.ajax({ | ||||
|             url: baseApiUrl + 'notes-history/' + noteId + "?encryption=" + (encrypt ? 0 : 1), | ||||
|             url: baseApiUrl + 'notes-history/' + noteId + "?encryption=" + (protect ? 0 : 1), | ||||
|             type: 'GET', | ||||
|             error: () => showError("Error getting note history.") | ||||
|         }); | ||||
|  | ||||
|         for (const row of result) { | ||||
|             if (encrypt) { | ||||
|             if (protect) { | ||||
|                 row.note_title = encryptString(row.note_title); | ||||
|                 row.note_text = encryptString(row.note_text); | ||||
|             } | ||||
| @@ -222,7 +211,7 @@ const encryption = (function() { | ||||
|                 row.note_text = decryptString(row.note_text); | ||||
|             } | ||||
|  | ||||
|             row.encryption = encrypt ? 1 : 0; | ||||
|             row.is_protected = protect; | ||||
|  | ||||
|             await $.ajax({ | ||||
|                 url: baseApiUrl + 'notes-history', | ||||
| @@ -236,14 +225,14 @@ const encryption = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async function decryptNoteAndSendToServer() { | ||||
|         await ensureEncryptionIsAvailable(true, true); | ||||
|     async function unprotectNoteAndSendToServer() { | ||||
|         await ensureProtectedSession(true, true); | ||||
|  | ||||
|         const note = noteEditor.getCurrentNote(); | ||||
|  | ||||
|         noteEditor.updateNoteFromInputs(note); | ||||
|  | ||||
|         note.detail.encryption = 0; | ||||
|         note.detail.is_protected = false; | ||||
|  | ||||
|         await noteEditor.saveNoteToServer(note); | ||||
|  | ||||
| @@ -253,13 +242,13 @@ const encryption = (function() { | ||||
|     } | ||||
|  | ||||
|     async function encryptSubTree(noteId) { | ||||
|         await ensureEncryptionIsAvailable(true, true); | ||||
|         await ensureProtectedSession(true, true); | ||||
|  | ||||
|         updateSubTreeRecursively(noteId, note => { | ||||
|                 if (note.detail.encryption === null || note.detail.encryption === 0) { | ||||
|                 if (!note.detail.is_protected) { | ||||
|                     encryptNote(note); | ||||
|  | ||||
|                     note.detail.encryption = 1; | ||||
|                     note.detail.is_protected = true; | ||||
|  | ||||
|                     return true; | ||||
|                 } | ||||
| @@ -280,13 +269,13 @@ const encryption = (function() { | ||||
|     } | ||||
|  | ||||
|     async function decryptSubTree(noteId) { | ||||
|         await ensureEncryptionIsAvailable(true, true); | ||||
|         await ensureProtectedSession(true, true); | ||||
|  | ||||
|         updateSubTreeRecursively(noteId, note => { | ||||
|                 if (note.detail.encryption === 1) { | ||||
|                 if (note.detail.is_protected) { | ||||
|                     decryptNote(note); | ||||
|  | ||||
|                     note.detail.encryption = 0; | ||||
|                     note.detail.is_protected = false; | ||||
|  | ||||
|                     return true; | ||||
|                 } | ||||
| @@ -368,14 +357,13 @@ const encryption = (function() { | ||||
|  | ||||
|     return { | ||||
|         setEncryptionSessionTimeout, | ||||
|         ensureEncryptionIsAvailable, | ||||
|         ensureProtectedSession, | ||||
|         resetEncryptionSession, | ||||
|         isEncryptionAvailable, | ||||
|         encryptNoteIfNecessary, | ||||
|         encryptString, | ||||
|         decryptString, | ||||
|         encryptNoteAndSendToServer, | ||||
|         decryptNoteAndSendToServer, | ||||
|         protectNoteAndSendToServer, | ||||
|         unprotectNoteAndSendToServer, | ||||
|         encryptSubTree, | ||||
|         decryptSubTree, | ||||
|         getProtectedSessionId | ||||
|   | ||||
| @@ -59,8 +59,6 @@ const noteEditor = (function() { | ||||
|  | ||||
|         updateNoteFromInputs(note); | ||||
|  | ||||
|         encryption.encryptNoteIfNecessary(note); | ||||
|  | ||||
|         await saveNoteToServer(note); | ||||
|     } | ||||
|  | ||||
| @@ -70,7 +68,7 @@ const noteEditor = (function() { | ||||
|  | ||||
|         note.detail.note_text = contents; | ||||
|  | ||||
|         if (!note.detail.encryption) { | ||||
|         if (!note.detail.is_protected) { | ||||
|             const linkRegexp = /<a[^>]+?href="[^"]*app#([A-Za-z0-9]{22})"[^>]*?>[^<]+?<\/a>/g; | ||||
|             let match; | ||||
|  | ||||
| @@ -170,11 +168,11 @@ const noteEditor = (function() { | ||||
|  | ||||
|     function setTreeBasedOnEncryption(note) { | ||||
|         const node = treeUtils.getNodeByKey(note.detail.note_id); | ||||
|         node.toggleClass("encrypted", note.detail.encryption > 0); | ||||
|         node.toggleClass("encrypted", note.detail.is_protected); | ||||
|     } | ||||
|  | ||||
|     function setNoteBackgroundIfEncrypted(note) { | ||||
|         if (note.detail.encryption > 0) { | ||||
|         if (note.detail.is_protected) { | ||||
|             $(".note-editable").addClass("encrypted"); | ||||
|             encryptButton.hide(); | ||||
|             decryptButton.show(); | ||||
| @@ -197,7 +195,7 @@ const noteEditor = (function() { | ||||
|             noteTitleEl.focus().select(); | ||||
|         } | ||||
|  | ||||
|         await encryption.ensureEncryptionIsAvailable(currentNote.detail.encryption > 0, false); | ||||
|         await encryption.ensureProtectedSession(currentNote.detail.is_protected, false); | ||||
|  | ||||
|         noteDetailWrapperEl.show(); | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,7 @@ const noteTree = (function() { | ||||
|  | ||||
|             note.title = note.note_title; | ||||
|  | ||||
|             if (note.encryption > 0) { | ||||
|             if (note.is_protected) { | ||||
|                 note.extraClasses = "encrypted"; | ||||
|             } | ||||
|             else { | ||||
| @@ -63,7 +63,7 @@ const noteTree = (function() { | ||||
|                 noteEditor.createNote(node, parentKey, 'after', encryption); | ||||
|             }, | ||||
|             "ctrl+insert": node => { | ||||
|                 noteEditor.createNote(node, node.key, 'into', node.data.encryption); | ||||
|                 noteEditor.createNote(node, node.key, 'into', node.data.is_protected); | ||||
|             }, | ||||
|             "del": node => { | ||||
|                 treeChanges.deleteNode(node); | ||||
|   | ||||
| @@ -8,7 +8,7 @@ const treeUtils = (function() { | ||||
|     } | ||||
|  | ||||
|     function getParentEncryption(node) { | ||||
|         return node.getParent() === null ? 0 : node.getParent().data.encryption; | ||||
|         return node.getParent() === null ? 0 : node.getParent().data.is_protected; | ||||
|     } | ||||
|  | ||||
|     function getNodeByKey(noteId) { | ||||
|   | ||||
| @@ -7,15 +7,15 @@ const auth = require('../../services/auth'); | ||||
|  | ||||
| router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||
|     const noteId = req.params.noteId; | ||||
|     const encryption = req.query.encryption; | ||||
|     const isProtected = req.query.is_protected; | ||||
|  | ||||
|     let history; | ||||
|  | ||||
|     if (encryption === undefined) { | ||||
|     if (isProtected === undefined) { | ||||
|         history = await sql.getResults("select * from notes_history where note_id = ? order by date_modified_to desc", [noteId]); | ||||
|     } | ||||
|     else { | ||||
|         history = await sql.getResults("select * from notes_history where note_id = ? and encryption = ? order by date_modified_to desc", [noteId, encryption]); | ||||
|         history = await sql.getResults("select * from notes_history where note_id = ? and is_protected = ? order by date_modified_to desc", [noteId, is_protected]); | ||||
|     } | ||||
|  | ||||
|     res.send(history); | ||||
|   | ||||
| @@ -9,6 +9,7 @@ const utils = require('../../services/utils'); | ||||
| const notes = require('../../services/notes'); | ||||
| const protected_session = require('../../services/protected_session'); | ||||
| const data_encryption = require('../../services/data_encryption'); | ||||
| const RequestContext = require('../../services/request_context'); | ||||
|  | ||||
| router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||
|     let noteId = req.params.noteId; | ||||
| @@ -17,12 +18,7 @@ router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||
|  | ||||
|     let detail = await sql.getSingleResult("select * from notes where note_id = ?", [noteId]); | ||||
|  | ||||
|     if (detail.note_clone_id) { | ||||
|         noteId = detail.note_clone_id; | ||||
|         detail = sql.getSingleResult("select * from notes where note_id = ?", [noteId]); | ||||
|     } | ||||
|  | ||||
|     if (detail.encryption > 0) { | ||||
|     if (detail.is_protected) { | ||||
|         const dataKey = protected_session.getDataKey(req); | ||||
|  | ||||
|         detail.note_title = data_encryption.decrypt(dataKey, detail.note_title); | ||||
| @@ -49,11 +45,11 @@ router.post('/:parentNoteId/children', async (req, res, next) => { | ||||
| }); | ||||
|  | ||||
| router.put('/:noteId', async (req, res, next) => { | ||||
|     const newNote = req.body; | ||||
|     const note = req.body; | ||||
|     let noteId = req.params.noteId; | ||||
|     const browserId = utils.browserId(req); | ||||
|     const reqCtx = new RequestContext(req); | ||||
|  | ||||
|     await notes.updateNote(noteId, newNote, browserId); | ||||
|     await notes.updateNote(noteId, note, reqCtx); | ||||
|  | ||||
|     res.send({}); | ||||
| }); | ||||
|   | ||||
| @@ -13,13 +13,10 @@ const data_encryption = require('../../services/data_encryption'); | ||||
| router.get('/', auth.checkApiAuth, async (req, res, next) => { | ||||
|     const notes = await sql.getResults("select " | ||||
|         + "notes_tree.*, " | ||||
|         + "COALESCE(clone.note_title, notes.note_title) as note_title, " | ||||
|         + "notes.note_clone_id, " | ||||
|         + "notes.encryption, " | ||||
|         + "case when notes.note_clone_id is null or notes.note_clone_id = '' then 0 else 1 end as is_clone " | ||||
|         + "notes.note_title, " | ||||
|         + "notes.is_protected " | ||||
|         + "from notes_tree " | ||||
|         + "join notes on notes.note_id = notes_tree.note_id " | ||||
|         + "left join notes as clone on notes.note_clone_id = clone.note_id " | ||||
|         + "where notes.is_deleted = 0 and notes_tree.is_deleted = 0 " | ||||
|         + "order by note_pid, note_pos"); | ||||
|  | ||||
| @@ -29,7 +26,7 @@ router.get('/', auth.checkApiAuth, async (req, res, next) => { | ||||
|     const dataKey = protected_session.getDataKey(req); | ||||
|  | ||||
|     for (const note of notes) { | ||||
|         if (note['encryption']) { | ||||
|         if (note.is_protected) { | ||||
|             note.note_title = data_encryption.decrypt(dataKey, note.note_title); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ module.exports = { | ||||
|     CREATE_NOTE: 'CREATE', | ||||
|     DELETE_NOTE: 'DELETE', | ||||
|     CHANGE_PARENT: 'PARENT', | ||||
|     ENCRYPTION: 'ENCRYPTION', | ||||
|     PROTECTED: 'PROTECTED', | ||||
|     CHANGE_PASSWORD: 'PASSWORD', | ||||
|     SETTINGS: 'SETTINGS', | ||||
|     SYNC: 'SYNC' | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| const protected_session = require('./protected_session'); | ||||
| "use strict"; | ||||
|  | ||||
| const utils = require('./utils'); | ||||
| const aesjs = require('./aes'); | ||||
| const sha256 = require('./sha256'); | ||||
|  | ||||
| function getProtectedSessionId(req) { | ||||
|     return req.headers['x-protected-session-id']; | ||||
| @@ -10,6 +12,15 @@ function getDataAes(dataKey) { | ||||
|     return new aesjs.ModeOfOperation.ctr(dataKey, new aesjs.Counter(5)); | ||||
| } | ||||
|  | ||||
| function arraysIdentical(a, b) { | ||||
|     let i = a.length; | ||||
|     if (i !== b.length) return false; | ||||
|     while (i--) { | ||||
|         if (a[i] !== b[i]) return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function decrypt(dataKey, encryptedBase64) { | ||||
|     if (!dataKey) { | ||||
|         return "[protected]"; | ||||
| @@ -24,10 +35,42 @@ function decrypt(dataKey, encryptedBase64) { | ||||
|     const digest = decryptedBytes.slice(0, 4); | ||||
|     const payload = decryptedBytes.slice(4); | ||||
|  | ||||
|     const hashArray = sha256Array(payload); | ||||
|  | ||||
|     const computedDigest = hashArray.slice(0, 4); | ||||
|  | ||||
|     if (!arraysIdentical(digest, computedDigest)) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return aesjs.utils.utf8.fromBytes(payload); | ||||
| } | ||||
|  | ||||
| function encrypt(dataKey, plainText) { | ||||
|     if (!dataKey) { | ||||
|         throw new Error("No data key!"); | ||||
|     } | ||||
|  | ||||
|     const aes = getDataAes(dataKey); | ||||
|  | ||||
|     const payload = Array.from(aesjs.utils.utf8.toBytes(plainText)); | ||||
|     const digest = sha256Array(payload).slice(0, 4); | ||||
|  | ||||
|     const digestWithPayload = digest.concat(payload); | ||||
|  | ||||
|     const encryptedBytes = aes.encrypt(digestWithPayload); | ||||
|  | ||||
|     return utils.toBase64(encryptedBytes); | ||||
| } | ||||
|  | ||||
| function sha256Array(content) { | ||||
|     const hash = sha256.create(); | ||||
|     hash.update(content); | ||||
|     return hash.array(); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getProtectedSessionId, | ||||
|     decrypt | ||||
|     decrypt, | ||||
|     encrypt | ||||
| }; | ||||
| @@ -4,7 +4,7 @@ const options = require('./options'); | ||||
| const fs = require('fs-extra'); | ||||
| const log = require('./log'); | ||||
|  | ||||
| const APP_DB_VERSION = 27; | ||||
| const APP_DB_VERSION = 28; | ||||
| const MIGRATIONS_DIR = "./migrations"; | ||||
|  | ||||
| async function migrate() { | ||||
|   | ||||
| @@ -3,6 +3,7 @@ const options = require('./options'); | ||||
| const utils = require('./utils'); | ||||
| const notes = require('./notes'); | ||||
| const audit_category = require('./audit_category'); | ||||
| const data_encryption = require('./data_encryption'); | ||||
|  | ||||
| async function createNewNote(parentNoteId, note, browserId) { | ||||
|     const noteId = utils.newNoteId(); | ||||
| @@ -46,10 +47,9 @@ async function createNewNote(parentNoteId, note, browserId) { | ||||
|             'note_id': noteId, | ||||
|             'note_title': note.note_title, | ||||
|             'note_text': '', | ||||
|             'note_clone_id': '', | ||||
|             'date_created': now, | ||||
|             'date_modified': now, | ||||
|             'encryption': note.encryption | ||||
|             'is_protected': note.is_protected | ||||
|         }); | ||||
|  | ||||
|         await sql.insert("notes_tree", { | ||||
| @@ -64,13 +64,17 @@ async function createNewNote(parentNoteId, note, browserId) { | ||||
|     return noteId; | ||||
| } | ||||
|  | ||||
| async function updateNote(noteId, newNote, browserId) { | ||||
|     const origNoteDetail = await sql.getSingleResult("select * from notes where note_id = ?", [noteId]); | ||||
|  | ||||
|     if (origNoteDetail.note_clone_id) { | ||||
|         noteId = origNoteDetail.note_clone_id; | ||||
| async function encryptNote(note, ctx) { | ||||
|     note.detail.note_title = data_encryption.encrypt(ctx.getDataKey(), note.detail.note_title); | ||||
|     note.detail.note_text = data_encryption.encrypt(ctx.getDataKey(), note.detail.note_text); | ||||
| } | ||||
|  | ||||
| async function updateNote(noteId, newNote, ctx) { | ||||
|     if (newNote.detail.is_protected) { | ||||
|         await encryptNote(newNote, ctx); | ||||
|     } | ||||
|  | ||||
|     const origNoteDetail = await sql.getSingleResult("select * from notes where note_id = ?", [noteId]); | ||||
|  | ||||
|     const now = utils.nowTimestamp(); | ||||
|  | ||||
| @@ -82,10 +86,10 @@ async function updateNote(noteId, newNote, browserId) { | ||||
|  | ||||
|     await sql.doInTransaction(async () => { | ||||
|         if (noteHistoryId) { | ||||
|             await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where note_history_id = ?", [ | ||||
|             await sql.execute("update notes_history set note_title = ?, note_text = ?, is_protected = ?, date_modified_to = ? where note_history_id = ?", [ | ||||
|                 newNote.detail.note_title, | ||||
|                 newNote.detail.note_text, | ||||
|                 newNote.detail.encryption, | ||||
|                 newNote.detail.is_protected, | ||||
|                 now, | ||||
|                 noteHistoryId | ||||
|             ]); | ||||
| @@ -93,25 +97,25 @@ async function updateNote(noteId, newNote, browserId) { | ||||
|         else { | ||||
|             noteHistoryId = utils.randomString(16); | ||||
|  | ||||
|             await sql.execute("insert into notes_history (note_history_id, note_id, note_title, note_text, encryption, date_modified_from, date_modified_to) " + | ||||
|             await sql.execute("insert into notes_history (note_history_id, note_id, note_title, note_text, is_protected, date_modified_from, date_modified_to) " + | ||||
|                 "values (?, ?, ?, ?, ?, ?, ?)", [ | ||||
|                 noteHistoryId, | ||||
|                 noteId, | ||||
|                 newNote.detail.note_title, | ||||
|                 newNote.detail.note_text, | ||||
|                 newNote.detail.encryption, | ||||
|                 newNote.detail.is_protected, | ||||
|                 now, | ||||
|                 now | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         await sql.addNoteHistorySync(noteHistoryId); | ||||
|         await addNoteAudits(origNoteDetail, newNote.detail, browserId); | ||||
|         await addNoteAudits(origNoteDetail, newNote.detail, ctx.browserId); | ||||
|  | ||||
|         await sql.execute("update notes set note_title = ?, note_text = ?, encryption = ?, date_modified = ? where note_id = ?", [ | ||||
|         await sql.execute("update notes set note_title = ?, note_text = ?, is_protected = ?, date_modified = ? where note_id = ?", [ | ||||
|             newNote.detail.note_title, | ||||
|             newNote.detail.note_text, | ||||
|             newNote.detail.encryption, | ||||
|             newNote.detail.is_protected, | ||||
|             now, | ||||
|             noteId]); | ||||
|  | ||||
| @@ -147,10 +151,10 @@ async function addNoteAudits(origNote, newNote, browserId) { | ||||
|         await sql.addAudit(audit_category.UPDATE_CONTENT, browserId, noteId); | ||||
|     } | ||||
|  | ||||
|     if (!origNote || newNote.encryption !== origNote.encryption) { | ||||
|         const origEncryption = origNote ? origNote.encryption : null; | ||||
|     if (!origNote || newNote.is_protected !== origNote.is_protected) { | ||||
|         const origIsProtected = origNote ? origNote.is_protected : null; | ||||
|  | ||||
|         await sql.addAudit(audit_category.ENCRYPTION, browserId, noteId, origEncryption, newNote.encryption); | ||||
|         await sql.addAudit(audit_category.PROTECTED, browserId, noteId, origIsProtected, newNote.is_protected); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const utils = require('./utils'); | ||||
|  | ||||
| function setDataKey(req, decryptedDataKey) { | ||||
| @@ -7,8 +9,12 @@ function setDataKey(req, decryptedDataKey) { | ||||
|     return req.session.protectedSessionId; | ||||
| } | ||||
|  | ||||
| function getProtectedSessionId(req) { | ||||
|     return req.headers['x-protected-session-id']; | ||||
| } | ||||
|  | ||||
| function getDataKey(req) { | ||||
|     const protectedSessionId = req.headers['x-protected-session-id']; | ||||
|     const protectedSessionId = getProtectedSessionId(req); | ||||
|  | ||||
|     if (protectedSessionId && req.session.protectedSessionId === protectedSessionId) { | ||||
|         return req.session.decryptedDataKey; | ||||
| @@ -18,7 +24,14 @@ function getDataKey(req) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function isProtectedSessionAvailable(req) { | ||||
|     const protectedSessionId = getProtectedSessionId(req); | ||||
|  | ||||
|     return protectedSessionId && req.session.protectedSessionId === protectedSessionId; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     setDataKey, | ||||
|     getDataKey | ||||
|     getDataKey, | ||||
|     isProtectedSessionAvailable | ||||
| }; | ||||
							
								
								
									
										25
									
								
								services/request_context.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								services/request_context.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const protected_session = require('./protected_session'); | ||||
|  | ||||
| module.exports = function(req) { | ||||
|     const browserId = req.headers['x-browser-id']; | ||||
|  | ||||
|     function isProtectedSessionAvailable() { | ||||
|         return protected_session.isProtectedSessionAvailable(req); | ||||
|     } | ||||
|  | ||||
|     function getDataKey() { | ||||
|         if (!isProtectedSessionAvailable()) { | ||||
|             throw new Error("Protected session is not available"); | ||||
|         } | ||||
|  | ||||
|         return protected_session.getDataKey(req); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         browserId, | ||||
|         isProtectedSessionAvailable, | ||||
|         getDataKey | ||||
|     }; | ||||
| }; | ||||
							
								
								
									
										1
									
								
								services/sha256.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/sha256.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -239,6 +239,10 @@ async function syncRequest(syncContext, method, uri, body) { | ||||
| if (isSyncSetup) { | ||||
|     log.info("Setting up sync to " + SYNC_SERVER + " with timeout " + SYNC_TIMEOUT); | ||||
|  | ||||
|     if (SYNC_PROXY) { | ||||
|         log.info("Sync proxy: " + SYNC_PROXY); | ||||
|     } | ||||
|  | ||||
|     setInterval(sync, 60000); | ||||
|  | ||||
|     // kickoff initial sync immediately | ||||
|   | ||||
| @@ -68,7 +68,7 @@ | ||||
|  | ||||
|       <div class="hide-toggle" style="grid-area: title;"> | ||||
|         <div style="display: flex; align-items: center;"> | ||||
|           <a onclick="encryption.encryptNoteAndSendToServer()" | ||||
|           <a onclick="encryption.protectNoteAndSendToServer()" | ||||
|              title="Encrypt the note so that password will be required to view the note" | ||||
|              class="icon-action" | ||||
|              id="encrypt-button" | ||||
| @@ -76,7 +76,7 @@ | ||||
|             <img src="images/icons/lock.png" alt="Encrypt note"/> | ||||
|           </a> | ||||
|  | ||||
|           <a onclick="encryption.decryptNoteAndSendToServer()" | ||||
|           <a onclick="encryption.unprotectNoteAndSendToServer()" | ||||
|              title="Decrypt note permamently so that password will not be required to access this note in the future" | ||||
|              class="icon-action" | ||||
|              id="decrypt-button" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user