diff --git a/package-lock.json b/package-lock.json index 2c7112a53..bc45e090d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "trilium", - "version": "0.30.4", + "version": "0.30.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index 52ac85d69..9f64d56ab 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -17,7 +17,8 @@ import link from './services/link.js'; import messagingService from './services/messaging.js'; import noteDetailService from './services/note_detail.js'; import noteType from './services/note_type.js'; -import protected_session from './services/protected_session.js'; +import protectedSessionService from './services/protected_session.js'; +import protectedSessionHolder from './services/protected_session_holder.js'; import searchNotesService from './services/search_notes.js'; import FrontendScriptApi from './services/frontend_script_api.js'; import ScriptContext from './services/script_context.js'; @@ -52,6 +53,8 @@ window.glob.getCurrentNote = noteDetailService.getCurrentNote; window.glob.requireLibrary = libraryLoader.requireLibrary; window.glob.ESLINT = libraryLoader.ESLINT; +protectedSessionHolder.setProtectedSessionId(null); + window.onerror = function (msg, url, lineNo, columnNo, error) { const string = msg.toLowerCase(); diff --git a/src/public/javascripts/dialogs/export.js b/src/public/javascripts/dialogs/export.js index 9c8bdc8fc..3ea317154 100644 --- a/src/public/javascripts/dialogs/export.js +++ b/src/public/javascripts/dialogs/export.js @@ -79,7 +79,7 @@ $form.submit(() => { function exportBranch(branchId, type, format, version) { exportId = utils.randomString(10); - const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}/${version}/${exportId}?protectedSessionId=` + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); + const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}/${version}/${exportId}`; utils.download(url); } diff --git a/src/public/javascripts/services/note_detail_file.js b/src/public/javascripts/services/note_detail_file.js index df8e48328..23f8e9f06 100644 --- a/src/public/javascripts/services/note_detail_file.js +++ b/src/public/javascripts/services/note_detail_file.js @@ -51,8 +51,7 @@ $openButton.click(() => { function getFileUrl() { // electron needs absolute URL so we extract current host, port, protocol - return utils.getHost() + "/api/notes/" + noteDetailService.getCurrentNoteId() - + "/download?protectedSessionId=" + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); + return utils.getHost() + "/api/notes/" + noteDetailService.getCurrentNoteId(); } export default { diff --git a/src/public/javascripts/services/note_detail_image.js b/src/public/javascripts/services/note_detail_image.js index 592d382c0..8e5769a74 100644 --- a/src/public/javascripts/services/note_detail_image.js +++ b/src/public/javascripts/services/note_detail_image.js @@ -62,8 +62,7 @@ $copyToClipboardButton.click(() => { function getFileUrl() { // electron needs absolute URL so we extract current host, port, protocol - return utils.getHost() + "/api/notes/" + noteDetailService.getCurrentNoteId() - + "/download?protectedSessionId=" + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); + return utils.getHost() + "/api/notes/" + noteDetailService.getCurrentNoteId() + "/download"; } export default { diff --git a/src/public/javascripts/services/protected_session_holder.js b/src/public/javascripts/services/protected_session_holder.js index b95a8c8d0..89a6f4324 100644 --- a/src/public/javascripts/services/protected_session_holder.js +++ b/src/public/javascripts/services/protected_session_holder.js @@ -1,9 +1,10 @@ import utils from "./utils.js"; import optionsInitService from './options_init.js'; +const PROTECTED_SESSION_ID_KEY = 'protectedSessionId'; + let lastProtectedSessionOperationDate = null; let protectedSessionTimeout = null; -let protectedSessionId = null; optionsInitService.optionsReady.then(options => protectedSessionTimeout = options.protectedSessionTimeout); @@ -17,16 +18,13 @@ function setProtectedSessionTimeout(encSessTimeout) { protectedSessionTimeout = encSessTimeout; } -function getProtectedSessionId() { - return protectedSessionId; -} - function setProtectedSessionId(id) { - protectedSessionId = id; + // using session cookie so that it disappears after browser/tab is closed + utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, id); } function resetProtectedSession() { - protectedSessionId = null; + utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, null); // most secure solution - guarantees nothing remained in memory // since this expires because user doesn't use the app, it shouldn't be disruptive @@ -34,17 +32,16 @@ function resetProtectedSession() { } function isProtectedSessionAvailable() { - return protectedSessionId !== null; + return !!utils.getCookie(PROTECTED_SESSION_ID_KEY); } function touchProtectedSession() { if (isProtectedSessionAvailable()) { - lastProtectedSessionOperationDate = new Date(); + setProtectedSessionId(utils.getCookie(PROTECTED_SESSION_ID_KEY)); } } export default { - getProtectedSessionId, setProtectedSessionId, resetProtectedSession, isProtectedSessionAvailable, diff --git a/src/public/javascripts/services/server.js b/src/public/javascripts/services/server.js index 9fb4279ca..6dc7236f3 100644 --- a/src/public/javascripts/services/server.js +++ b/src/public/javascripts/services/server.js @@ -3,18 +3,10 @@ import utils from './utils.js'; import infoService from "./info.js"; function getHeaders() { - let protectedSessionId = null; - - try { // this is because protected session might not be declared in some cases - protectedSessionId = protectedSessionHolder.getProtectedSessionId(); - } - catch(e) {} - // headers need to be lowercase because node.js automatically converts them to lower case // so hypothetical protectedSessionId becomes protectedsessionid on the backend // also avoiding using underscores instead of dashes since nginx filters them out by default return { - 'trilium-protected-session-id': protectedSessionId, 'trilium-source-id': glob.sourceId }; } diff --git a/src/public/javascripts/services/utils.js b/src/public/javascripts/services/utils.js index ecd404d14..d831a9e5c 100644 --- a/src/public/javascripts/services/utils.js +++ b/src/public/javascripts/services/utils.js @@ -164,11 +164,23 @@ function isDesktop() { || (!window.device && !/Mobi/.test(navigator.userAgent)); } +// cookie code below works for simple use cases only - ASCII only +// not setting path so that cookies do not leak into other websites if multiplexed with reverse proxy + function setCookie(name, value) { const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); const expires = "; expires=" + date.toUTCString(); - document.cookie = name + "=" + (value || "") + expires + "; path=/"; + document.cookie = name + "=" + (value || "") + expires + ";"; +} + +function setSessionCookie(name, value) { + document.cookie = name + "=" + (value || "") + ";"; +} + +function getCookie(name) { + const valueMatch = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); + return valueMatch ? valueMatch[2] : null; } function getNoteTypeClass(type) { @@ -213,6 +225,8 @@ export default { isMobile, isDesktop, setCookie, + setSessionCookie, + getCookie, getNoteTypeClass, getMimeTypeClass }; \ No newline at end of file diff --git a/src/services/protected_session.js b/src/services/protected_session.js index 283e04279..5a24d5ef9 100644 --- a/src/services/protected_session.js +++ b/src/services/protected_session.js @@ -15,7 +15,7 @@ function setDataKey(decryptedDataKey) { } function setProtectedSessionId(req) { - cls.namespace.set('protectedSessionId', req.headers['trilium-protected-session-id'] || req.query.protectedSessionId); + cls.namespace.set('protectedSessionId', req.cookies.protectedSessionId); } function getProtectedSessionId() {