mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
protected session is now global application state to avoid weird issues with multiple tabs/windows/reloads
This commit is contained in:
parent
fddab59265
commit
de6108f95d
@ -119,8 +119,6 @@ const appContext = new AppContext(window.glob.isMainWindow);
|
|||||||
|
|
||||||
// we should save all outstanding changes before the page/app is closed
|
// we should save all outstanding changes before the page/app is closed
|
||||||
$(window).on('beforeunload', () => {
|
$(window).on('beforeunload', () => {
|
||||||
protectedSessionHolder.resetSessionCookie();
|
|
||||||
|
|
||||||
let allSaved = true;
|
let allSaved = true;
|
||||||
|
|
||||||
appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter(wr => !!wr.deref());
|
appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter(wr => !!wr.deref());
|
||||||
|
@ -67,8 +67,6 @@ function setupGlobs() {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
protectedSessionHolder.setProtectedSessionId(null);
|
|
||||||
|
|
||||||
for (const appCssNoteId of glob.appCssNoteIds || []) {
|
for (const appCssNoteId of glob.appCssNoteIds || []) {
|
||||||
libraryLoader.requireCss(`api/notes/download/${appCssNoteId}`);
|
libraryLoader.requireCss(`api/notes/download/${appCssNoteId}`);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import utils from './utils.js';
|
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
import protectedSessionHolder from './protected_session_holder.js';
|
import protectedSessionHolder from './protected_session_holder.js';
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import ws from "./ws.js";
|
import ws from "./ws.js";
|
||||||
import appContext from "./app_context.js";
|
import appContext from "./app_context.js";
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
|
import utils from "./utils.js";
|
||||||
|
|
||||||
let protectedSessionDeferred = null;
|
let protectedSessionDeferred = null;
|
||||||
|
|
||||||
async function leaveProtectedSession() {
|
async function leaveProtectedSession() {
|
||||||
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
protectedSessionHolder.resetProtectedSession();
|
await protectedSessionHolder.resetProtectedSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,16 +41,18 @@ async function reloadData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setupProtectedSession(password) {
|
async function setupProtectedSession(password) {
|
||||||
const response = await enterProtectedSessionOnServer(password);
|
const response = await server.post('login/protected', { password: password });
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
toastService.showError("Wrong password.", 3000);
|
toastService.showError("Wrong password.", 3000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
protectedSessionHolder.enableProtectedSession();
|
||||||
protectedSessionHolder.touchProtectedSession();
|
}
|
||||||
|
|
||||||
|
ws.subscribeToMessages(async message => {
|
||||||
|
if (message.type === 'protectedSessionLogin') {
|
||||||
await reloadData();
|
await reloadData();
|
||||||
|
|
||||||
await appContext.triggerEvent('treeCacheReloaded');
|
await appContext.triggerEvent('treeCacheReloaded');
|
||||||
@ -66,12 +68,10 @@ async function setupProtectedSession(password) {
|
|||||||
|
|
||||||
toastService.showMessage("Protected session has been started.");
|
toastService.showMessage("Protected session has been started.");
|
||||||
}
|
}
|
||||||
|
else if (message.type === 'protectedSessionLogout') {
|
||||||
async function enterProtectedSessionOnServer(password) {
|
utils.reloadApp();
|
||||||
return await server.post('login/protected', {
|
|
||||||
password: password
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function protectNote(noteId, protect, includingSubtree) {
|
async function protectNote(noteId, protect, includingSubtree) {
|
||||||
await enterProtectedSession();
|
await enterProtectedSession();
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import utils from "./utils.js";
|
|
||||||
import options from './options.js';
|
import options from './options.js';
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
|
|
||||||
const PROTECTED_SESSION_ID_KEY = 'protectedSessionId';
|
|
||||||
|
|
||||||
let lastProtectedSessionOperationDate = 0;
|
let lastProtectedSessionOperationDate = 0;
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@ -15,32 +12,23 @@ setInterval(() => {
|
|||||||
}
|
}
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
function setProtectedSessionId(id) {
|
function enableProtectedSession() {
|
||||||
// using session cookie so that it disappears after browser/tab is closed
|
glob.isProtectedSessionAvailable = true;
|
||||||
utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetSessionCookie() {
|
touchProtectedSession();
|
||||||
utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resetProtectedSession() {
|
async function resetProtectedSession() {
|
||||||
resetSessionCookie();
|
|
||||||
|
|
||||||
await server.post("logout/protected");
|
await server.post("logout/protected");
|
||||||
|
|
||||||
utils.reloadApp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProtectedSessionAvailable() {
|
function isProtectedSessionAvailable() {
|
||||||
return !!utils.getCookie(PROTECTED_SESSION_ID_KEY);
|
return glob.isProtectedSessionAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchProtectedSession() {
|
function touchProtectedSession() {
|
||||||
if (isProtectedSessionAvailable()) {
|
if (isProtectedSessionAvailable()) {
|
||||||
lastProtectedSessionOperationDate = Date.now();
|
lastProtectedSessionOperationDate = Date.now();
|
||||||
|
|
||||||
setProtectedSessionId(utils.getCookie(PROTECTED_SESSION_ID_KEY));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +39,7 @@ function touchProtectedSessionIfNecessary(note) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setProtectedSessionId,
|
enableProtectedSession,
|
||||||
resetSessionCookie,
|
|
||||||
resetProtectedSession,
|
resetProtectedSession,
|
||||||
isProtectedSessionAvailable,
|
isProtectedSessionAvailable,
|
||||||
touchProtectedSession,
|
touchProtectedSession,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "./basic_widget.js";
|
||||||
import HistoryNavigationWidget from "./history_navigation.js";
|
import HistoryNavigationWidget from "./history_navigation.js";
|
||||||
|
import protectedSessionHolder from "../services/protected_session_holder.js";
|
||||||
import protectedSessionService from "../services/protected_session.js";
|
import protectedSessionService from "../services/protected_session.js";
|
||||||
import QuickSearchWidget from "./quick_search.js";
|
import QuickSearchWidget from "./quick_search.js";
|
||||||
|
|
||||||
@ -68,8 +69,7 @@ const TPL = `
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn btn-sm leave-protected-session-button noborder"
|
<button class="btn btn-sm leave-protected-session-button noborder"
|
||||||
title="Leave protected session so that protected notes are not accessible any more."
|
title="Leave protected session so that protected notes are not accessible any more.">
|
||||||
style="display: none;">
|
|
||||||
<span class="bx bx-log-out"></span>
|
<span class="bx bx-log-out"></span>
|
||||||
|
|
||||||
Leave protected session
|
Leave protected session
|
||||||
@ -96,9 +96,11 @@ export default class StandardTopWidget extends BasicWidget {
|
|||||||
|
|
||||||
this.$enterProtectedSessionButton = this.$widget.find(".enter-protected-session-button");
|
this.$enterProtectedSessionButton = this.$widget.find(".enter-protected-session-button");
|
||||||
this.$enterProtectedSessionButton.on('click', protectedSessionService.enterProtectedSession);
|
this.$enterProtectedSessionButton.on('click', protectedSessionService.enterProtectedSession);
|
||||||
|
this.$enterProtectedSessionButton.toggle(!protectedSessionHolder.isProtectedSessionAvailable());
|
||||||
|
|
||||||
this.$leaveProtectedSessionButton = this.$widget.find(".leave-protected-session-button");
|
this.$leaveProtectedSessionButton = this.$widget.find(".leave-protected-session-button");
|
||||||
this.$leaveProtectedSessionButton.on('click', protectedSessionService.leaveProtectedSession);
|
this.$leaveProtectedSessionButton.on('click', protectedSessionService.leaveProtectedSession);
|
||||||
|
this.$leaveProtectedSessionButton.toggle(protectedSessionHolder.isProtectedSessionAvailable());
|
||||||
|
|
||||||
return this.$widget;
|
return this.$widget;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import treeService from "../../services/tree.js";
|
|||||||
import noteCreateService from "../../services/note_create.js";
|
import noteCreateService from "../../services/note_create.js";
|
||||||
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
|
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
|
||||||
|
|
||||||
const ENABLE_INSPECTOR = true;
|
const ENABLE_INSPECTOR = false;
|
||||||
|
|
||||||
const mentionSetup = {
|
const mentionSetup = {
|
||||||
feeds: [
|
feeds: [
|
||||||
@ -121,7 +121,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
||||||
console.log('glob.isDev', glob.isDev);
|
|
||||||
if (glob.isDev && ENABLE_INSPECTOR) {
|
if (glob.isDev && ENABLE_INSPECTOR) {
|
||||||
await import(/* webpackIgnore: true */'../../../libraries/ckeditor/inspector.js');
|
await import(/* webpackIgnore: true */'../../../libraries/ckeditor/inspector.js');
|
||||||
CKEditorInspector.attach(this.textEditor);
|
CKEditorInspector.attach(this.textEditor);
|
||||||
|
@ -8,11 +8,11 @@ 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 eventService = require('../../services/events');
|
||||||
const cls = require('../../services/cls');
|
|
||||||
const sqlInit = require('../../services/sql_init');
|
const sqlInit = require('../../services/sql_init');
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const optionService = require('../../services/options');
|
const optionService = require('../../services/options');
|
||||||
const ApiToken = require('../../entities/api_token');
|
const ApiToken = require('../../entities/api_token');
|
||||||
|
const ws = require("../../services/ws.js");
|
||||||
|
|
||||||
function loginSync(req) {
|
function loginSync(req) {
|
||||||
if (!sqlInit.schemaExists()) {
|
if (!sqlInit.schemaExists()) {
|
||||||
@ -65,16 +65,14 @@ function loginToProtectedSession(req) {
|
|||||||
|
|
||||||
const decryptedDataKey = passwordEncryptionService.getDataKey(password);
|
const decryptedDataKey = passwordEncryptionService.getDataKey(password);
|
||||||
|
|
||||||
const protectedSessionId = protectedSessionService.setDataKey(decryptedDataKey);
|
protectedSessionService.setDataKey(decryptedDataKey);
|
||||||
|
|
||||||
// this is set here so that event handlers have access to the protected session
|
|
||||||
cls.set('protectedSessionId', protectedSessionId);
|
|
||||||
|
|
||||||
eventService.emit(eventService.ENTER_PROTECTED_SESSION);
|
eventService.emit(eventService.ENTER_PROTECTED_SESSION);
|
||||||
|
|
||||||
|
ws.sendMessageToAllClients({ type: 'protectedSessionLogin' });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true
|
||||||
protectedSessionId: protectedSessionId
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +80,8 @@ function logoutFromProtectedSession() {
|
|||||||
protectedSessionService.resetDataKey();
|
protectedSessionService.resetDataKey();
|
||||||
|
|
||||||
eventService.emit(eventService.LEAVE_PROTECTED_SESSION);
|
eventService.emit(eventService.LEAVE_PROTECTED_SESSION);
|
||||||
|
|
||||||
|
ws.sendMessageToAllClients({ type: 'protectedSessionLogout' });
|
||||||
}
|
}
|
||||||
|
|
||||||
function token(req) {
|
function token(req) {
|
||||||
|
@ -7,6 +7,7 @@ const config = require('../services/config');
|
|||||||
const optionService = require('../services/options');
|
const optionService = require('../services/options');
|
||||||
const log = require('../services/log');
|
const log = require('../services/log');
|
||||||
const env = require('../services/env');
|
const env = require('../services/env');
|
||||||
|
const protectedSessionService = require("../services/protected_session.js");
|
||||||
|
|
||||||
function index(req, res) {
|
function index(req, res) {
|
||||||
const options = optionService.getOptionsMap();
|
const options = optionService.getOptionsMap();
|
||||||
@ -30,7 +31,8 @@ function index(req, res) {
|
|||||||
appCssNoteIds: getAppCssNoteIds(),
|
appCssNoteIds: getAppCssNoteIds(),
|
||||||
isDev: env.isDev(),
|
isDev: env.isDev(),
|
||||||
isMainWindow: !req.query.extra,
|
isMainWindow: !req.query.extra,
|
||||||
extraHoistedNoteId: req.query.extraHoistedNoteId
|
extraHoistedNoteId: req.query.extraHoistedNoteId,
|
||||||
|
isProtectedSessionAvailable: protectedSessionService.isProtectedSessionAvailable()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,6 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
|
|||||||
cls.set('sourceId', req.headers['trilium-source-id']);
|
cls.set('sourceId', req.headers['trilium-source-id']);
|
||||||
cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']);
|
cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']);
|
||||||
cls.set('hoistedNoteId', req.headers['trilium-hoisted-note-id'] || 'root');
|
cls.set('hoistedNoteId', req.headers['trilium-hoisted-note-id'] || 'root');
|
||||||
protectedSessionService.setProtectedSessionId(req);
|
|
||||||
|
|
||||||
const cb = () => routeHandler(req, res, next);
|
const cb = () => routeHandler(req, res, next);
|
||||||
|
|
||||||
|
@ -1,42 +1,24 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const utils = require('./utils');
|
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
const dataEncryptionService = require('./data_encryption');
|
const dataEncryptionService = require('./data_encryption');
|
||||||
const cls = require('./cls');
|
|
||||||
|
|
||||||
let dataKeyMap = {};
|
let dataKey = null;
|
||||||
|
|
||||||
function setDataKey(decryptedDataKey) {
|
function setDataKey(decryptedDataKey) {
|
||||||
const protectedSessionId = utils.randomSecureToken(32);
|
dataKey = Array.from(decryptedDataKey);
|
||||||
|
|
||||||
dataKeyMap[protectedSessionId] = Array.from(decryptedDataKey); // can't store buffer in session
|
|
||||||
|
|
||||||
return protectedSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProtectedSessionId(req) {
|
|
||||||
cls.set('protectedSessionId', req.cookies.protectedSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProtectedSessionId() {
|
|
||||||
return cls.get('protectedSessionId');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDataKey() {
|
function getDataKey() {
|
||||||
const protectedSessionId = getProtectedSessionId();
|
return dataKey;
|
||||||
|
|
||||||
return dataKeyMap[protectedSessionId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetDataKey() {
|
function resetDataKey() {
|
||||||
dataKeyMap = {};
|
dataKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProtectedSessionAvailable() {
|
function isProtectedSessionAvailable() {
|
||||||
const protectedSessionId = getProtectedSessionId();
|
return !!dataKey;
|
||||||
|
|
||||||
return !!dataKeyMap[protectedSessionId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function decryptNotes(notes) {
|
function decryptNotes(notes) {
|
||||||
@ -74,12 +56,10 @@ function decryptString(cipherText) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setDataKey,
|
setDataKey,
|
||||||
getDataKey,
|
|
||||||
resetDataKey,
|
resetDataKey,
|
||||||
isProtectedSessionAvailable,
|
isProtectedSessionAvailable,
|
||||||
encrypt,
|
encrypt,
|
||||||
decrypt,
|
decrypt,
|
||||||
decryptString,
|
decryptString,
|
||||||
decryptNotes,
|
decryptNotes
|
||||||
setProtectedSessionId
|
|
||||||
};
|
};
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
||||||
isMainWindow: <%= isMainWindow %>,
|
isMainWindow: <%= isMainWindow %>,
|
||||||
extraHoistedNoteId: '<%= extraHoistedNoteId %>',
|
extraHoistedNoteId: '<%= extraHoistedNoteId %>',
|
||||||
|
isProtectedSessionAvailable: <%= isProtectedSessionAvailable %>,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user