set password from web trilium

This commit is contained in:
zadam 2021-12-29 23:37:12 +01:00
parent 4e31af8c84
commit f92016f9ec
16 changed files with 58 additions and 50 deletions

View File

@ -0,0 +1 @@
DELETE FROM options WHERE name = 'username';

View File

@ -85,14 +85,10 @@ function logoutFromProtectedSession() {
} }
function token(req) { function token(req) {
const username = req.body.username;
const password = req.body.password; const password = req.body.password;
const isUsernameValid = username === optionService.getOption('username'); if (!passwordEncryptionService.verifyPassword(password)) {
const isPasswordValid = passwordEncryptionService.verifyPassword(password); return [401, "Incorrect password"];
if (!isUsernameValid || !isPasswordValid) {
return [401, "Incorrect username/password"];
} }
const apiToken = new ApiToken({ const apiToken = new ApiToken({

View File

@ -6,7 +6,6 @@ const searchService = require('../../services/search/services/search');
// options allowed to be updated directly in options dialog // options allowed to be updated directly in options dialog
const ALLOWED_OPTIONS = new Set([ const ALLOWED_OPTIONS = new Set([
'username', // not exposed for update (not harmful anyway), needed for reading
'eraseEntitiesAfterTimeInSeconds', 'eraseEntitiesAfterTimeInSeconds',
'protectedSessionTimeout', 'protectedSessionTimeout',
'noteRevisionSnapshotTimeInterval', 'noteRevisionSnapshotTimeInterval',

View File

@ -18,9 +18,9 @@ async function setupNewDocument() {
} }
function setupSyncFromServer(req) { function setupSyncFromServer(req) {
const { syncServerHost, syncProxy, username, password } = req.body; const { syncServerHost, syncProxy, password } = req.body;
return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, username, password); return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password);
} }
function saveSyncSeed(req) { function saveSyncSeed(req) {

View File

@ -4,21 +4,48 @@ const utils = require('../services/utils');
const optionService = require('../services/options'); const optionService = require('../services/options');
const myScryptService = require('../services/my_scrypt'); const myScryptService = require('../services/my_scrypt');
const log = require('../services/log'); const log = require('../services/log');
const sqlInit = require("../services/sql_init.js");
const optionsInitService = require("../services/options_init.js");
function loginPage(req, res) { function loginPage(req, res) {
res.render('login', { failedAuth: false }); res.render('login', { failedAuth: false });
} }
function setPasswordPage(req, res) { function setPasswordPage(req, res) {
res.render('set_password', { failed: false }); res.render('set_password', { error: false });
}
function setPassword(req, res) {
if (sqlInit.isPasswordSet()) {
return [400, "Password has been already set"];
}
let {password1, password2} = req.body;
password1 = password1.trim();
password2 = password2.trim();
let error;
if (password1 !== password2) {
error = "Entered passwords don't match.";
} else if (password1.length < 4) {
error = "Password must be at least 4 characters long.";
}
if (error) {
res.render('set_password', { error });
return;
}
optionsInitService.initPassword(password1);
res.redirect('login');
} }
function login(req, res) { function login(req, res) {
const userName = optionService.getOption('username');
const guessedPassword = req.body.password; const guessedPassword = req.body.password;
if (req.body.username === userName && verifyPassword(guessedPassword)) { if (verifyPassword(guessedPassword)) {
const rememberMe = req.body.remember_me; const rememberMe = req.body.remember_me;
req.session.regenerate(() => { req.session.regenerate(() => {
@ -34,7 +61,7 @@ function login(req, res) {
} }
else { else {
// note that logged IP address is usually meaningless since the traffic should come from a reverse proxy // note that logged IP address is usually meaningless since the traffic should come from a reverse proxy
log.info(`WARNING: Wrong username / password from ${req.ip}, rejecting.`); log.info(`WARNING: Wrong password from ${req.ip}, rejecting.`);
res.render('login', {'failedAuth': true}); res.render('login', {'failedAuth': true});
} }
@ -60,6 +87,7 @@ function logout(req, res) {
module.exports = { module.exports = {
loginPage, loginPage,
setPasswordPage, setPasswordPage,
setPassword,
login, login,
logout logout
}; };

View File

@ -183,7 +183,7 @@ const uploadMiddleware = multer.single('upload');
function register(app) { function register(app) {
route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index); route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index);
route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage); route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage);
route(GET, '/set_password', [auth.checkAppInitialized], loginRoute.setPasswordPage); route(GET, '/set-password', [auth.checkAppInitialized], loginRoute.setPasswordPage);
const loginRateLimiter = rateLimit({ const loginRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes windowMs: 15 * 60 * 1000, // 15 minutes
@ -192,6 +192,7 @@ function register(app) {
route(POST, '/login', [loginRateLimiter], loginRoute.login); route(POST, '/login', [loginRateLimiter], loginRoute.login);
route(POST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout); route(POST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout);
route(POST, '/set-password', [auth.checkAppInitialized], loginRoute.setPassword);
route(GET, '/setup', [], setupRoute.setupPage); route(GET, '/setup', [], setupRoute.setupPage);
apiRoute(GET, '/api/tree', treeApiRoute.getTree); apiRoute(GET, '/api/tree', treeApiRoute.getTree);

View File

@ -4,8 +4,8 @@ const build = require('./build');
const packageJson = require('../../package'); const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir'); const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 188; const APP_DB_VERSION = 189;
const SYNC_VERSION = 23; const SYNC_VERSION = 24;
const CLIPPER_PROTOCOL_VERSION = "1.0"; const CLIPPER_PROTOCOL_VERSION = "1.0";
module.exports = { module.exports = {

View File

@ -18,7 +18,7 @@ function checkAuth(req, res, next) {
if (sqlInit.isPasswordSet()) { if (sqlInit.isPasswordSet()) {
res.redirect("login"); res.redirect("login");
} else { } else {
res.redirect("set_password"); res.redirect("set-password");
} }
} }
else { else {
@ -57,7 +57,7 @@ function checkAppInitialized(req, res, next) {
function checkPasswordSet(req, res, next) { function checkPasswordSet(req, res, next) {
if (!utils.isElectron() && !sqlInit.isPasswordSet()) { if (!utils.isElectron() && !sqlInit.isPasswordSet()) {
res.redirect("set_password"); res.redirect("set-password");
} else { } else {
next(); next();
} }
@ -99,10 +99,10 @@ function checkCredentials(req, res, next) {
const auth = new Buffer.from(header, 'base64').toString(); const auth = new Buffer.from(header, 'base64').toString();
const [username, password] = auth.split(/:/); const [username, password] = auth.split(/:/);
const dbUsername = optionService.getOption('username'); // username is ignored
if (dbUsername !== username || !passwordEncryptionService.verifyPassword(password)) { if (!passwordEncryptionService.verifyPassword(password)) {
res.status(401).send('Incorrect username and/or password'); res.status(401).send('Incorrect password');
} }
else { else {
next(); next();

View File

@ -12,7 +12,7 @@ function initDocumentOptions() {
optionService.createOption('documentSecret', utils.randomSecureToken(16), false); optionService.createOption('documentSecret', utils.randomSecureToken(16), false);
} }
function initPassword(username, password) { function initPassword(password) {
optionService.createOption('passwordVerificationSalt', utils.randomSecureToken(32), true); optionService.createOption('passwordVerificationSalt', utils.randomSecureToken(32), true);
optionService.createOption('passwordDerivedKeySalt', utils.randomSecureToken(32), true); optionService.createOption('passwordDerivedKeySalt', utils.randomSecureToken(32), true);

View File

@ -38,7 +38,7 @@ function exec(opts) {
}; };
if (opts.auth) { if (opts.auth) {
headers['trilium-cred'] = Buffer.from(opts.auth.username + ":" + opts.auth.password).toString('base64'); headers['trilium-cred'] = Buffer.from("dummy:" + opts.auth.password).toString('base64');
} }
const request = client.request({ const request = client.request({

View File

@ -55,7 +55,7 @@ async function requestToSyncServer(method, path, body = null) {
}), timeout); }), timeout);
} }
async function setupSyncFromSyncServer(syncServerHost, syncProxy, username, password) { async function setupSyncFromSyncServer(syncServerHost, syncProxy, password) {
if (sqlInit.isDbInitialized()) { if (sqlInit.isDbInitialized()) {
return { return {
result: 'failure', result: 'failure',
@ -70,10 +70,7 @@ async function setupSyncFromSyncServer(syncServerHost, syncProxy, username, pass
const resp = await request.exec({ const resp = await request.exec({
method: 'get', method: 'get',
url: syncServerHost + '/api/setup/sync-seed', url: syncServerHost + '/api/setup/sync-seed',
auth: { auth: { password },
username,
password
},
proxy: syncProxy, proxy: syncProxy,
timeout: 30000 // seed request should not take long timeout: 30000 // seed request should not take long
}); });

View File

@ -31,11 +31,7 @@ function isDbInitialized() {
} }
function isPasswordSet() { function isPasswordSet() {
const value = sql.getValue("SELECT value FROM options WHERE name = 'passwordVerificationHash'"); return !!sql.getValue("SELECT value FROM options WHERE name = 'passwordVerificationHash'");
console.log("AAAAAAAAAAAAEEEEEEEEE", value);
return !!value;
} }
async function initDbConnection() { async function initDbConnection() {

View File

@ -20,7 +20,7 @@
<a class="nav-link" data-toggle="tab" href="#options-code-notes">Code notes</a> <a class="nav-link" data-toggle="tab" href="#options-code-notes">Code notes</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#options-credentials">Username & password</a> <a class="nav-link" data-toggle="tab" href="#options-credentials">Password</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#options-backup">Backup</a> <a class="nav-link" data-toggle="tab" href="#options-backup">Backup</a>

View File

@ -14,17 +14,11 @@
<% if (failedAuth) { %> <% if (failedAuth) { %>
<div class="alert alert-warning"> <div class="alert alert-warning">
Username and / or password are incorrect. Please try again. Password is incorrect. Please try again.
</div> </div>
<% } %> <% } %>
<form action="login" method="POST"> <form action="login" method="POST">
<div class="form-group">
<label for="username">Username</label>
<div class="controls">
<input id="username" name="username" placeholder="" class="form-control" type="text">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label for="password">Password</label>
<div class="controls"> <div class="controls">
@ -76,4 +70,4 @@
<link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet">
</body> </body>
</html> </html>

View File

@ -12,15 +12,15 @@
<div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto" style="padding-top: 25px;"> <div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto" style="padding-top: 25px;">
<h1>Set password</h1> <h1>Set password</h1>
<% if (failed) { %> <% if (error) { %>
<div class="alert alert-warning"> <div class="alert alert-warning">
Err <%= error %>
</div> </div>
<% } %> <% } %>
<p>Before you can start using Trilium from web, you need to set a password first. You will then use this password to login.</p> <p>Before you can start using Trilium from web, you need to set a password first. You will then use this password to login.</p>
<form action="login" method="POST"> <form action="set-password" method="POST">
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label for="password">Password</label>
<div class="controls"> <div class="controls">

View File

@ -118,10 +118,6 @@
<p><strong>Note:</strong> If you leave proxy setting blank, system proxy will be used (applies to desktop/electron build only)</p> <p><strong>Note:</strong> If you leave proxy setting blank, system proxy will be used (applies to desktop/electron build only)</p>
</div> </div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" class="form-control" data-bind="value: username" placeholder="Username">
</div>
<div class="form-group"> <div class="form-group">
<label for="password1">Password</label> <label for="password1">Password</label>
<input type="password" id="password1" class="form-control" data-bind="value: password1" placeholder="Password"> <input type="password" id="password1" class="form-control" data-bind="value: password1" placeholder="Password">