mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02:00
resetting/setting password from options
This commit is contained in:
parent
f92016f9ec
commit
8120f1bf25
@ -3,14 +3,16 @@ import protectedSessionHolder from "../../services/protected_session_holder.js";
|
||||
import toastService from "../../services/toast.js";
|
||||
|
||||
const TPL = `
|
||||
<h3>Change password</h3>
|
||||
<h3 id="password-heading"></h3>
|
||||
|
||||
<div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;">
|
||||
Please take care to remember your new password. Password is used to encrypt protected notes. If you forget your password, then all your protected notes are forever lost with no recovery options.
|
||||
Please take care to remember your new password. Password is used to encrypt protected notes.
|
||||
If you forget your password, then all your protected notes are forever lost.
|
||||
In case you did forget your password, <a id="reset-password-button" href="javascript:">click here to reset it</a>.
|
||||
</div>
|
||||
|
||||
<form id="change-password-form">
|
||||
<div class="form-group">
|
||||
<div class="form-group" id="old-password-form-group">
|
||||
<label for="old-password">Old password</label>
|
||||
<input class="form-control" id="old-password" type="password">
|
||||
</div>
|
||||
@ -25,22 +27,41 @@ const TPL = `
|
||||
<input class="form-control" id="new-password2" type="password">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Change password</button>
|
||||
<button class="btn btn-primary" id="save-password-button">Change password</button>
|
||||
</form>`;
|
||||
|
||||
export default class ChangePasswordOptions {
|
||||
constructor() {
|
||||
$("#options-credentials").html(TPL);
|
||||
|
||||
this.$passwordHeading = $("#password-heading");
|
||||
this.$form = $("#change-password-form");
|
||||
this.$oldPassword = $("#old-password");
|
||||
this.$newPassword1 = $("#new-password1");
|
||||
this.$newPassword2 = $("#new-password2");
|
||||
this.$savePasswordButton = $("#save-password-button");
|
||||
this.$resetPasswordButton = $("#reset-password-button");
|
||||
|
||||
this.$resetPasswordButton.on("click", async () => {
|
||||
if (confirm("By resetting the password you will forever lose access to all your existing protected notes. Do you really want to reset the password?")) {
|
||||
await server.post("password/reset?really=yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes");
|
||||
|
||||
const options = await server.get('options');
|
||||
this.optionsLoaded(options);
|
||||
|
||||
alert("Password has been reset. Please set new password");
|
||||
}
|
||||
});
|
||||
|
||||
this.$form.on('submit', () => this.save());
|
||||
}
|
||||
|
||||
optionsLoaded(options) {
|
||||
const isPasswordSet = options.isPasswordSet === 'true';
|
||||
|
||||
$("#old-password-form-group").toggle(isPasswordSet);
|
||||
this.$passwordHeading.text(isPasswordSet ? 'Change password' : 'Set password');
|
||||
this.$savePasswordButton.text(isPasswordSet ? 'Change password' : 'Set password');
|
||||
}
|
||||
|
||||
save() {
|
||||
|
@ -67,6 +67,8 @@ function getOptions() {
|
||||
}
|
||||
}
|
||||
|
||||
resultMap['isPasswordSet'] = !!optionMap['passwordVerificationHash'] ? 'true' : 'false';
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
const changePasswordService = require('../../services/change_password');
|
||||
const passwordService = require('../../services/password.js');
|
||||
|
||||
function changePassword(req) {
|
||||
return changePasswordService.changePassword(req.body.current_password, req.body.new_password);
|
||||
if (passwordService.isPasswordSet()) {
|
||||
return passwordService.changePassword(req.body.current_password, req.body.new_password);
|
||||
}
|
||||
else {
|
||||
return passwordService.setPassword(req.body.new_password);
|
||||
}
|
||||
}
|
||||
|
||||
function resetPassword(req) {
|
||||
// protection against accidental call (not a security measure)
|
||||
if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") {
|
||||
return [400, "Incorrect password reset confirmation"];
|
||||
}
|
||||
|
||||
return passwordService.resetPassword();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
changePassword
|
||||
changePassword,
|
||||
resetPassword
|
||||
};
|
||||
|
@ -4,8 +4,7 @@ const utils = require('../services/utils');
|
||||
const optionService = require('../services/options');
|
||||
const myScryptService = require('../services/my_scrypt');
|
||||
const log = require('../services/log');
|
||||
const sqlInit = require("../services/sql_init.js");
|
||||
const optionsInitService = require("../services/options_init.js");
|
||||
const passwordService = require("../services/password.js");
|
||||
|
||||
function loginPage(req, res) {
|
||||
res.render('login', { failedAuth: false });
|
||||
@ -16,7 +15,7 @@ function setPasswordPage(req, res) {
|
||||
}
|
||||
|
||||
function setPassword(req, res) {
|
||||
if (sqlInit.isPasswordSet()) {
|
||||
if (passwordService.isPasswordSet()) {
|
||||
return [400, "Password has been already set"];
|
||||
}
|
||||
|
||||
@ -37,7 +36,7 @@ function setPassword(req, res) {
|
||||
return;
|
||||
}
|
||||
|
||||
optionsInitService.initPassword(password1);
|
||||
passwordService.setPassword(password1);
|
||||
|
||||
res.redirect('login');
|
||||
}
|
||||
|
@ -290,6 +290,7 @@ function register(app) {
|
||||
apiRoute(GET, '/api/options/user-themes', optionsApiRoute.getUserThemes);
|
||||
|
||||
apiRoute(POST, '/api/password/change', passwordApiRoute.changePassword);
|
||||
apiRoute(POST, '/api/password/reset', passwordApiRoute.resetPassword);
|
||||
|
||||
apiRoute(POST, '/api/sync/test', syncApiRoute.testSync);
|
||||
apiRoute(POST, '/api/sync/now', syncApiRoute.syncNow);
|
||||
|
@ -5,8 +5,8 @@ const log = require('./log');
|
||||
const sqlInit = require('./sql_init');
|
||||
const utils = require('./utils');
|
||||
const passwordEncryptionService = require('./password_encryption');
|
||||
const optionService = require('./options');
|
||||
const config = require('./config');
|
||||
const passwordService = require("./password.js");
|
||||
|
||||
const noAuthentication = config.General && config.General.noAuthentication === true;
|
||||
|
||||
@ -15,7 +15,7 @@ function checkAuth(req, res, next) {
|
||||
res.redirect("setup");
|
||||
}
|
||||
else if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
|
||||
if (sqlInit.isPasswordSet()) {
|
||||
if (passwordService.isPasswordSet()) {
|
||||
res.redirect("login");
|
||||
} else {
|
||||
res.redirect("set-password");
|
||||
@ -56,7 +56,7 @@ function checkAppInitialized(req, res, next) {
|
||||
}
|
||||
|
||||
function checkPasswordSet(req, res, next) {
|
||||
if (!utils.isElectron() && !sqlInit.isPasswordSet()) {
|
||||
if (!utils.isElectron() && !passwordService.isPasswordSet()) {
|
||||
res.redirect("set-password");
|
||||
} else {
|
||||
next();
|
||||
|
@ -1,37 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const sql = require('./sql');
|
||||
const optionService = require('./options');
|
||||
const myScryptService = require('./my_scrypt');
|
||||
const utils = require('./utils');
|
||||
const passwordEncryptionService = require('./password_encryption');
|
||||
|
||||
function changePassword(currentPassword, newPassword) {
|
||||
if (!passwordEncryptionService.verifyPassword(currentPassword)) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Given current password doesn't match hash"
|
||||
};
|
||||
}
|
||||
|
||||
sql.transactional(() => {
|
||||
const decryptedDataKey = passwordEncryptionService.getDataKey(currentPassword);
|
||||
|
||||
optionService.setOption('passwordVerificationSalt', utils.randomSecureToken(32));
|
||||
optionService.setOption('passwordDerivedKeySalt', utils.randomSecureToken(32));
|
||||
|
||||
const newPasswordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(newPassword));
|
||||
|
||||
passwordEncryptionService.setDataKey(newPassword, decryptedDataKey);
|
||||
|
||||
optionService.setOption('passwordVerificationHash', newPasswordVerificationKey);
|
||||
});
|
||||
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
changePassword
|
||||
};
|
@ -1,6 +1,4 @@
|
||||
const optionService = require('./options');
|
||||
const passwordEncryptionService = require('./password_encryption');
|
||||
const myScryptService = require('./my_scrypt');
|
||||
const appInfo = require('./app_info');
|
||||
const utils = require('./utils');
|
||||
const log = require('./log');
|
||||
@ -12,19 +10,6 @@ function initDocumentOptions() {
|
||||
optionService.createOption('documentSecret', utils.randomSecureToken(16), false);
|
||||
}
|
||||
|
||||
function initPassword(password) {
|
||||
optionService.createOption('passwordVerificationSalt', utils.randomSecureToken(32), true);
|
||||
optionService.createOption('passwordDerivedKeySalt', utils.randomSecureToken(32), true);
|
||||
|
||||
const passwordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(password), true);
|
||||
optionService.createOption('passwordVerificationHash', passwordVerificationKey, true);
|
||||
|
||||
// passwordEncryptionService expects these options to already exist
|
||||
optionService.createOption('encryptedDataKey', '', true);
|
||||
|
||||
passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16), true);
|
||||
}
|
||||
|
||||
function initNotSyncedOptions(initialized, opts = {}) {
|
||||
optionService.createOption('openTabs', JSON.stringify([
|
||||
{
|
||||
@ -127,7 +112,6 @@ function getKeyboardDefaultOptions() {
|
||||
|
||||
module.exports = {
|
||||
initDocumentOptions,
|
||||
initPassword,
|
||||
initNotSyncedOptions,
|
||||
initStartupOptions
|
||||
};
|
||||
|
83
src/services/password.js
Normal file
83
src/services/password.js
Normal file
@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
|
||||
const sql = require('./sql');
|
||||
const optionService = require('./options');
|
||||
const myScryptService = require('./my_scrypt');
|
||||
const utils = require('./utils');
|
||||
const passwordEncryptionService = require('./password_encryption');
|
||||
|
||||
function isPasswordSet() {
|
||||
return !!sql.getValue("SELECT value FROM options WHERE name = 'passwordVerificationHash'");
|
||||
}
|
||||
|
||||
function changePassword(currentPassword, newPassword) {
|
||||
if (!isPasswordSet()) {
|
||||
throw new Error("Password has not been set yet, so it cannot be changed. Use 'setPassword' instead.");
|
||||
}
|
||||
|
||||
if (!passwordEncryptionService.verifyPassword(currentPassword)) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Given current password doesn't match hash"
|
||||
};
|
||||
}
|
||||
|
||||
sql.transactional(() => {
|
||||
const decryptedDataKey = passwordEncryptionService.getDataKey(currentPassword);
|
||||
|
||||
optionService.setOption('passwordVerificationSalt', utils.randomSecureToken(32));
|
||||
optionService.setOption('passwordDerivedKeySalt', utils.randomSecureToken(32));
|
||||
|
||||
const newPasswordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(newPassword));
|
||||
|
||||
passwordEncryptionService.setDataKey(newPassword, decryptedDataKey);
|
||||
|
||||
optionService.setOption('passwordVerificationHash', newPasswordVerificationKey);
|
||||
});
|
||||
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
function setPassword(password) {
|
||||
if (isPasswordSet()) {
|
||||
throw new Error("Password is set already. Either change it or perform 'reset password' first.");
|
||||
}
|
||||
|
||||
optionService.createOption('passwordVerificationSalt', utils.randomSecureToken(32), true);
|
||||
optionService.createOption('passwordDerivedKeySalt', utils.randomSecureToken(32), true);
|
||||
|
||||
const passwordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(password), true);
|
||||
optionService.createOption('passwordVerificationHash', passwordVerificationKey, true);
|
||||
|
||||
// passwordEncryptionService expects these options to already exist
|
||||
optionService.createOption('encryptedDataKey', '', true);
|
||||
|
||||
passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16), true);
|
||||
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
function resetPassword() {
|
||||
// user forgot the password,
|
||||
sql.transactional(() => {
|
||||
optionService.setOption('passwordVerificationSalt', '');
|
||||
optionService.setOption('passwordDerivedKeySalt', '');
|
||||
optionService.setOption('encryptedDataKey', '');
|
||||
optionService.setOption('passwordVerificationHash', '');
|
||||
});
|
||||
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isPasswordSet,
|
||||
changePassword,
|
||||
setPassword,
|
||||
resetPassword
|
||||
};
|
@ -30,10 +30,6 @@ function isDbInitialized() {
|
||||
return initialized === 'true';
|
||||
}
|
||||
|
||||
function isPasswordSet() {
|
||||
return !!sql.getValue("SELECT value FROM options WHERE name = 'passwordVerificationHash'");
|
||||
}
|
||||
|
||||
async function initDbConnection() {
|
||||
if (!isDbInitialized()) {
|
||||
log.info(`DB not initialized, please visit setup page` +
|
||||
@ -93,6 +89,7 @@ async function createInitialDatabase() {
|
||||
optionsInitService.initDocumentOptions();
|
||||
optionsInitService.initNotSyncedOptions(true, {});
|
||||
optionsInitService.initStartupOptions();
|
||||
require("./password").resetPassword();
|
||||
});
|
||||
|
||||
log.info("Importing demo content ...");
|
||||
@ -175,6 +172,5 @@ module.exports = {
|
||||
isDbInitialized,
|
||||
createInitialDatabase,
|
||||
createDatabaseForSync,
|
||||
setDbAsInitialized,
|
||||
isPasswordSet
|
||||
setDbAsInitialized
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user