mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
Merge remote-tracking branch 'origin/stable' into next
# Conflicts: # package-lock.json # package.json # src/public/app/entities/note_short.js # src/public/app/services/protected_session.js # src/routes/api/login.js
This commit is contained in:
commit
051b9dff21
@ -58,7 +58,7 @@
|
||||
"mime-types": "2.1.30",
|
||||
"multer": "1.4.2",
|
||||
"node-abi": "2.26.0",
|
||||
"open": "8.0.6",
|
||||
"open": "8.0.8",
|
||||
"portscanner": "2.2.0",
|
||||
"rand-token": "1.0.1",
|
||||
"request": "^2.88.2",
|
||||
@ -80,8 +80,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "13.0.0-beta.18",
|
||||
"electron-builder": "22.10.5",
|
||||
"electron": "13.0.0-beta.23",
|
||||
"electron-builder": "22.11.1",
|
||||
"electron-packager": "15.2.0",
|
||||
"electron-rebuild": "2.3.5",
|
||||
"esm": "3.2.25",
|
||||
@ -90,7 +90,7 @@
|
||||
"lorem-ipsum": "2.0.3",
|
||||
"rcedit": "3.0.0",
|
||||
"webpack": "5.36.2",
|
||||
"webpack-cli": "4.6.0"
|
||||
"webpack-cli": "4.7.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"electron-installer-debian": "3.1.0"
|
||||
|
@ -3,6 +3,8 @@ import noteAttributeCache from "../services/note_attribute_cache.js";
|
||||
import ws from "../services/ws.js";
|
||||
import options from "../services/options.js";
|
||||
import froca from "../services/froca.js";
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import bundle from "../services/bundle.js";
|
||||
|
||||
const LABEL = 'label';
|
||||
const RELATION = 'relation';
|
||||
@ -701,6 +703,55 @@ class NoteShort {
|
||||
const labels = this.getLabels('workspaceTabBackgroundColor');
|
||||
return labels.length > 0 ? labels[0].value : "";
|
||||
}
|
||||
|
||||
/** @returns {boolean} true if this note is JavaScript (code or attachment) */
|
||||
isJavaScript() {
|
||||
return (this.type === "code" || this.type === "file")
|
||||
&& (this.mime.startsWith("application/javascript")
|
||||
|| this.mime === "application/x-javascript"
|
||||
|| this.mime === "text/javascript");
|
||||
}
|
||||
|
||||
/** @returns {boolean} true if this note is HTML */
|
||||
isHtml() {
|
||||
return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html";
|
||||
}
|
||||
|
||||
/** @returns {string|null} JS script environment - either "frontend" or "backend" */
|
||||
getScriptEnv() {
|
||||
if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) {
|
||||
return "frontend";
|
||||
}
|
||||
|
||||
if (this.type === 'render') {
|
||||
return "frontend";
|
||||
}
|
||||
|
||||
if (this.isJavaScript() && this.mime.endsWith('env=backend')) {
|
||||
return "backend";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async executeScript() {
|
||||
if (!this.isJavaScript()) {
|
||||
throw new Error(`Note ${this.noteId} is of type ${this.type} and mime ${this.mime} and thus cannot be executed`);
|
||||
}
|
||||
|
||||
const env = this.getScriptEnv();
|
||||
|
||||
if (env === "frontend") {
|
||||
const bundleService = (await import("../services/bundle.js")).default;
|
||||
await bundleService.getAndExecuteBundle(this.noteId);
|
||||
}
|
||||
else if (env === "backend") {
|
||||
await server.post('script/run/' + this.noteId);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unrecognized env type ${env} for note ${this.noteId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NoteShort;
|
||||
|
@ -119,8 +119,6 @@ const appContext = new AppContext(window.glob.isMainWindow);
|
||||
|
||||
// we should save all outstanding changes before the page/app is closed
|
||||
$(window).on('beforeunload', () => {
|
||||
protectedSessionHolder.resetSessionCookie();
|
||||
|
||||
let allSaved = true;
|
||||
|
||||
appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter(wr => !!wr.deref());
|
||||
|
@ -216,6 +216,7 @@ export default class Entrypoints extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: use note.executeScript()
|
||||
if (note.mime.endsWith("env=frontend")) {
|
||||
await bundleService.getAndExecuteBundle(note.noteId);
|
||||
} else if (note.mime.endsWith("env=backend")) {
|
||||
|
@ -68,8 +68,6 @@ function setupGlobs() {
|
||||
return false;
|
||||
};
|
||||
|
||||
protectedSessionHolder.setProtectedSessionId(null);
|
||||
|
||||
for (const appCssNoteId of glob.appCssNoteIds || []) {
|
||||
libraryLoader.requireCss(`api/notes/download/${appCssNoteId}`);
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import utils from './utils.js';
|
||||
import server from './server.js';
|
||||
import protectedSessionHolder from './protected_session_holder.js';
|
||||
import toastService from "./toast.js";
|
||||
import ws from "./ws.js";
|
||||
import appContext from "./app_context.js";
|
||||
import froca from "./froca.js";
|
||||
import utils from "./utils.js";
|
||||
|
||||
let protectedSessionDeferred = null;
|
||||
|
||||
async function leaveProtectedSession() {
|
||||
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
protectedSessionHolder.resetProtectedSession();
|
||||
await protectedSessionHolder.resetProtectedSession();
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,37 +41,37 @@ async function reloadData() {
|
||||
}
|
||||
|
||||
async function setupProtectedSession(password) {
|
||||
const response = await enterProtectedSessionOnServer(password);
|
||||
const response = await server.post('login/protected', { password: password });
|
||||
|
||||
if (!response.success) {
|
||||
toastService.showError("Wrong password.", 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
||||
protectedSessionHolder.touchProtectedSession();
|
||||
protectedSessionHolder.enableProtectedSession();
|
||||
}
|
||||
|
||||
await reloadData();
|
||||
ws.subscribeToMessages(async message => {
|
||||
if (message.type === 'protectedSessionLogin') {
|
||||
await reloadData();
|
||||
|
||||
await appContext.triggerEvent('frocaReloaded');
|
||||
|
||||
appContext.triggerEvent('protectedSessionStarted');
|
||||
appContext.triggerEvent('protectedSessionStarted');
|
||||
|
||||
if (protectedSessionDeferred !== null) {
|
||||
import("../dialogs/protected_session.js").then(dialog => dialog.close());
|
||||
if (protectedSessionDeferred !== null) {
|
||||
import("../dialogs/protected_session.js").then(dialog => dialog.close());
|
||||
|
||||
protectedSessionDeferred.resolve(true);
|
||||
protectedSessionDeferred = null;
|
||||
protectedSessionDeferred.resolve(true);
|
||||
protectedSessionDeferred = null;
|
||||
}
|
||||
|
||||
toastService.showMessage("Protected session has been started.");
|
||||
}
|
||||
|
||||
toastService.showMessage("Protected session has been started.");
|
||||
}
|
||||
|
||||
async function enterProtectedSessionOnServer(password) {
|
||||
return await server.post('login/protected', {
|
||||
password: password
|
||||
});
|
||||
}
|
||||
else if (message.type === 'protectedSessionLogout') {
|
||||
utils.reloadApp();
|
||||
}
|
||||
});
|
||||
|
||||
async function protectNote(noteId, protect, includingSubtree) {
|
||||
await enterProtectedSession();
|
||||
|
@ -1,9 +1,6 @@
|
||||
import utils from "./utils.js";
|
||||
import options from './options.js';
|
||||
import server from "./server.js";
|
||||
|
||||
const PROTECTED_SESSION_ID_KEY = 'protectedSessionId';
|
||||
|
||||
let lastProtectedSessionOperationDate = 0;
|
||||
|
||||
setInterval(() => {
|
||||
@ -15,32 +12,23 @@ setInterval(() => {
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
function setProtectedSessionId(id) {
|
||||
// using session cookie so that it disappears after browser/tab is closed
|
||||
utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, id);
|
||||
}
|
||||
function enableProtectedSession() {
|
||||
glob.isProtectedSessionAvailable = true;
|
||||
|
||||
function resetSessionCookie() {
|
||||
utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, null);
|
||||
touchProtectedSession();
|
||||
}
|
||||
|
||||
async function resetProtectedSession() {
|
||||
resetSessionCookie();
|
||||
|
||||
await server.post("logout/protected");
|
||||
|
||||
utils.reloadApp();
|
||||
}
|
||||
|
||||
function isProtectedSessionAvailable() {
|
||||
return !!utils.getCookie(PROTECTED_SESSION_ID_KEY);
|
||||
return glob.isProtectedSessionAvailable;
|
||||
}
|
||||
|
||||
function touchProtectedSession() {
|
||||
if (isProtectedSessionAvailable()) {
|
||||
lastProtectedSessionOperationDate = Date.now();
|
||||
|
||||
setProtectedSessionId(utils.getCookie(PROTECTED_SESSION_ID_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,8 +39,7 @@ function touchProtectedSessionIfNecessary(note) {
|
||||
}
|
||||
|
||||
export default {
|
||||
setProtectedSessionId,
|
||||
resetSessionCookie,
|
||||
enableProtectedSession,
|
||||
resetProtectedSession,
|
||||
isProtectedSessionAvailable,
|
||||
touchProtectedSession,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import BasicWidget from "./basic_widget.js";
|
||||
import HistoryNavigationWidget from "./history_navigation.js";
|
||||
import protectedSessionHolder from "../services/protected_session_holder.js";
|
||||
import protectedSessionService from "../services/protected_session.js";
|
||||
import QuickSearchWidget from "./quick_search.js";
|
||||
|
||||
@ -68,8 +69,7 @@ const TPL = `
|
||||
</button>
|
||||
|
||||
<button class="btn btn-sm leave-protected-session-button noborder"
|
||||
title="Leave protected session so that protected notes are not accessible any more."
|
||||
style="display: none;">
|
||||
title="Leave protected session so that protected notes are not accessible any more.">
|
||||
<span class="bx bx-log-out"></span>
|
||||
|
||||
Leave protected session
|
||||
@ -96,9 +96,11 @@ export default class StandardTopWidget extends BasicWidget {
|
||||
|
||||
this.$enterProtectedSessionButton = this.$widget.find(".enter-protected-session-button");
|
||||
this.$enterProtectedSessionButton.on('click', protectedSessionService.enterProtectedSession);
|
||||
this.$enterProtectedSessionButton.toggle(!protectedSessionHolder.isProtectedSessionAvailable());
|
||||
|
||||
this.$leaveProtectedSessionButton = this.$widget.find(".leave-protected-session-button");
|
||||
this.$leaveProtectedSessionButton.on('click', protectedSessionService.leaveProtectedSession);
|
||||
this.$leaveProtectedSessionButton.toggle(protectedSessionHolder.isProtectedSessionAvailable());
|
||||
|
||||
return this.$widget;
|
||||
}
|
||||
|
@ -14,6 +14,10 @@ button.btn, button.btn-sm {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.btn-micro {
|
||||
padding: 0 10px 0 10px;
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background-color: var(--accented-background-color) !important;
|
||||
color: var(--muted-text-color) !important;
|
||||
|
@ -8,11 +8,12 @@ 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');
|
||||
const sqlInit = require('../../services/sql_init');
|
||||
const sql = require('../../services/sql');
|
||||
const optionService = require('../../services/options');
|
||||
const ApiToken = require('../../services/becca/entities/api_token.js');
|
||||
const ApiToken = require('../../entities/api_token');
|
||||
const ws = require("../../services/ws.js");
|
||||
|
||||
function loginSync(req) {
|
||||
if (!sqlInit.schemaExists()) {
|
||||
@ -65,16 +66,14 @@ function loginToProtectedSession(req) {
|
||||
|
||||
const decryptedDataKey = passwordEncryptionService.getDataKey(password);
|
||||
|
||||
const protectedSessionId = protectedSessionService.setDataKey(decryptedDataKey);
|
||||
|
||||
// this is set here so that event handlers have access to the protected session
|
||||
cls.set('protectedSessionId', protectedSessionId);
|
||||
protectedSessionService.setDataKey(decryptedDataKey);
|
||||
|
||||
eventService.emit(eventService.ENTER_PROTECTED_SESSION);
|
||||
|
||||
ws.sendMessageToAllClients({ type: 'protectedSessionLogin' });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
protectedSessionId: protectedSessionId
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
@ -82,6 +81,8 @@ function logoutFromProtectedSession() {
|
||||
protectedSessionService.resetDataKey();
|
||||
|
||||
eventService.emit(eventService.LEAVE_PROTECTED_SESSION);
|
||||
|
||||
ws.sendMessageToAllClients({ type: 'protectedSessionLogout' });
|
||||
}
|
||||
|
||||
function token(req) {
|
||||
|
@ -7,6 +7,7 @@ const config = require('../services/config');
|
||||
const optionService = require('../services/options');
|
||||
const log = require('../services/log');
|
||||
const env = require('../services/env');
|
||||
const protectedSessionService = require("../services/protected_session.js");
|
||||
|
||||
function index(req, res) {
|
||||
const options = optionService.getOptionsMap();
|
||||
@ -30,7 +31,8 @@ function index(req, res) {
|
||||
appCssNoteIds: getAppCssNoteIds(),
|
||||
isDev: env.isDev(),
|
||||
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('localNowDateTime', req.headers['trilium-local-now-datetime']);
|
||||
cls.set('hoistedNoteId', req.headers['trilium-hoisted-note-id'] || 'root');
|
||||
protectedSessionService.setProtectedSessionId(req);
|
||||
|
||||
const cb = () => routeHandler(req, res, next);
|
||||
|
||||
|
@ -1,42 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
const utils = require('./utils');
|
||||
const log = require('./log');
|
||||
const dataEncryptionService = require('./data_encryption');
|
||||
const cls = require('./cls');
|
||||
|
||||
let dataKeyMap = {};
|
||||
let dataKey = null;
|
||||
|
||||
function setDataKey(decryptedDataKey) {
|
||||
const protectedSessionId = utils.randomSecureToken(32);
|
||||
|
||||
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');
|
||||
dataKey = Array.from(decryptedDataKey);
|
||||
}
|
||||
|
||||
function getDataKey() {
|
||||
const protectedSessionId = getProtectedSessionId();
|
||||
|
||||
return dataKeyMap[protectedSessionId];
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
function resetDataKey() {
|
||||
dataKeyMap = {};
|
||||
dataKey = null;
|
||||
}
|
||||
|
||||
function isProtectedSessionAvailable() {
|
||||
const protectedSessionId = getProtectedSessionId();
|
||||
|
||||
return !!dataKeyMap[protectedSessionId];
|
||||
return !!dataKey;
|
||||
}
|
||||
|
||||
function decryptNotes(notes) {
|
||||
@ -74,12 +56,10 @@ function decryptString(cipherText) {
|
||||
|
||||
module.exports = {
|
||||
setDataKey,
|
||||
getDataKey,
|
||||
resetDataKey,
|
||||
isProtectedSessionAvailable,
|
||||
encrypt,
|
||||
decrypt,
|
||||
decryptString,
|
||||
decryptNotes,
|
||||
setProtectedSessionId
|
||||
decryptNotes
|
||||
};
|
||||
|
@ -56,6 +56,7 @@
|
||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
||||
isMainWindow: <%= isMainWindow %>,
|
||||
extraHoistedNoteId: '<%= extraHoistedNoteId %>',
|
||||
isProtectedSessionAvailable: <%= isProtectedSessionAvailable %>,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user