From fad51409a973159d2d64e3177bbf28cee8b16cb0 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 3 May 2024 08:05:35 -0700 Subject: [PATCH] Continued integrating TOTP. --- package-lock.json | 2 +- .../options/multi_factor_authentication.js | 50 ++-- src/routes/api/options.ts | 217 +++++++++--------- src/routes/api/totp.ts | 21 +- src/routes/routes.ts | 8 +- 5 files changed, 151 insertions(+), 147 deletions(-) diff --git a/package-lock.json b/package-lock.json index 037578fba..05afb6ee6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7855,7 +7855,7 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/ie/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "node_modules/isobject": { diff --git a/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js b/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js index 3aee5b6d1..e755c62bb 100644 --- a/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js +++ b/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js @@ -39,7 +39,7 @@ const TPL = `

Generate TOTP Secret


- + `; export default class MultiFactorAuthenticationOptions extends OptionsWidget { @@ -56,9 +56,6 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { this.$mfaHeadding.text("Multi-Factor Authentication"); this.generateKey(); - // var gen = require("speakeasy"); - // toastService.showMessage("***REMOVED***"); - this.$totpEnabled.on("change", async () => { this.updateCheckboxOption("totpEnabled", this.$totpEnabled); }); @@ -67,6 +64,10 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { this.generateKey(); }); + this.$saveTotpButton.on("click", async () => { + this.save(); + }); + this.$protectedSessionTimeout = this.$widget.find( ".protected-session-timeout-in-seconds" ); @@ -100,49 +101,30 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { // } optionsLoaded(options) { - // I need to make sure that this is actually pinging the server and that this information is being pulled - // because it is telling me "totpEnabled is not allowed to be changed" in a toast every time I check the box server.get("totp/enabled").then((result) => { if (result.success) { - console.log("Result message: " + typeof result.message); - console.log("Result message: " + result.message); - this.setCheckboxState(this.$totpEnabled, result.message); - - console.log("TOTP Status: " + typeof result.message); - - if (result.message) { - this.$totpSecretInput.prop("disabled", false); - this.$saveTotpButton.prop("disabled", false); - } else { - this.$totpSecretInput.prop("disabled", true); - this.$saveTotpButton.prop("disabled", true); - } + this.$totpEnabled.prop("checked", result.message); + this.$totpSecretInput.prop("disabled", !result.message); + this.$saveTotpButton.prop("disabled", !result.message); + this.$totpSecret.prop("disapbled", !result.message); } else { toastService.showError(result.message); } }); + server.get("totp/get").then((result) => { + this.$totpSecretInput.text(result.secret); + }); + this.$protectedSessionTimeout.val(options.protectedSessionTimeout); } save() { - const oldPassword = this.$oldPassword.val(); - const newPassword1 = this.$newPassword1.val(); - const newPassword2 = this.$newPassword2.val(); - - this.$oldPassword.val(""); - this.$newPassword1.val(""); - this.$newPassword2.val(""); - - if (newPassword1 !== newPassword2) { - toastService.showError("New passwords are not the same."); - return false; - } + // TODO: CHECK VALIDITY OF SECRET server - .post("password/change", { - current_password: oldPassword, - new_password: newPassword1, + .post("totp/set", { + secret: this.$totpSecretInput.val(), }) .then((result) => { if (result.success) { diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts index 24d7b35ad..97eab092c 100644 --- a/src/routes/api/options.ts +++ b/src/routes/api/options.ts @@ -1,145 +1,152 @@ "use strict"; -import optionService = require('../../services/options'); -import log = require('../../services/log'); -import searchService = require('../../services/search/services/search'); -import ValidationError = require('../../errors/validation_error'); -import { Request } from 'express'; +import optionService = require("../../services/options"); +import log = require("../../services/log"); +import searchService = require("../../services/search/services/search"); +import ValidationError = require("../../errors/validation_error"); +import { Request } from "express"; // options allowed to be updated directly in the Options dialog const ALLOWED_OPTIONS = new Set([ - 'eraseEntitiesAfterTimeInSeconds', - 'protectedSessionTimeout', - 'revisionSnapshotTimeInterval', - 'zoomFactor', - 'theme', - 'syncServerHost', - 'syncServerTimeout', - 'syncProxy', - 'hoistedNoteId', - 'mainFontSize', - 'mainFontFamily', - 'treeFontSize', - 'treeFontFamily', - 'detailFontSize', - 'detailFontFamily', - 'monospaceFontSize', - 'monospaceFontFamily', - 'openNoteContexts', - 'vimKeymapEnabled', - 'codeLineWrapEnabled', - 'codeNotesMimeTypes', - 'spellCheckEnabled', - 'spellCheckLanguageCode', - 'imageMaxWidthHeight', - 'imageJpegQuality', - 'leftPaneWidth', - 'rightPaneWidth', - 'leftPaneVisible', - 'rightPaneVisible', - 'nativeTitleBarVisible', - 'headingStyle', - 'autoCollapseNoteTree', - 'autoReadonlySizeText', - 'autoReadonlySizeCode', - 'overrideThemeFonts', - 'dailyBackupEnabled', - 'weeklyBackupEnabled', - 'monthlyBackupEnabled', - 'maxContentWidth', - 'compressImages', - 'downloadImagesAutomatically', - 'minTocHeadings', - 'highlightsList', - 'checkForUpdates', - 'disableTray', - 'eraseUnusedAttachmentsAfterSeconds', - 'disableTray', - 'customSearchEngineName', - 'customSearchEngineUrl', - 'promotedAttributesOpenInRibbon', - 'editedNotesOpenInRibbon' + "eraseEntitiesAfterTimeInSeconds", + "protectedSessionTimeout", + "revisionSnapshotTimeInterval", + "zoomFactor", + "theme", + "syncServerHost", + "syncServerTimeout", + "syncProxy", + "hoistedNoteId", + "mainFontSize", + "mainFontFamily", + "treeFontSize", + "treeFontFamily", + "detailFontSize", + "detailFontFamily", + "monospaceFontSize", + "monospaceFontFamily", + "openNoteContexts", + "vimKeymapEnabled", + "codeLineWrapEnabled", + "codeNotesMimeTypes", + "spellCheckEnabled", + "spellCheckLanguageCode", + "imageMaxWidthHeight", + "imageJpegQuality", + "leftPaneWidth", + "rightPaneWidth", + "leftPaneVisible", + "rightPaneVisible", + "nativeTitleBarVisible", + "headingStyle", + "autoCollapseNoteTree", + "autoReadonlySizeText", + "autoReadonlySizeCode", + "overrideThemeFonts", + "dailyBackupEnabled", + "weeklyBackupEnabled", + "monthlyBackupEnabled", + "maxContentWidth", + "compressImages", + "downloadImagesAutomatically", + "minTocHeadings", + "highlightsList", + "checkForUpdates", + "disableTray", + "eraseUnusedAttachmentsAfterSeconds", + "disableTray", + "customSearchEngineName", + "customSearchEngineUrl", + "promotedAttributesOpenInRibbon", + "editedNotesOpenInRibbon", + "totpEnabled", ]); function getOptions() { - const optionMap = optionService.getOptionMap(); - const resultMap: Record = {}; + const optionMap = optionService.getOptionMap(); + const resultMap: Record = {}; - for (const optionName in optionMap) { - if (isAllowed(optionName)) { - resultMap[optionName] = optionMap[optionName]; - } + for (const optionName in optionMap) { + if (isAllowed(optionName)) { + resultMap[optionName] = optionMap[optionName]; } + } - resultMap['isPasswordSet'] = optionMap['passwordVerificationHash'] ? 'true' : 'false'; + resultMap["isPasswordSet"] = optionMap["passwordVerificationHash"] + ? "true" + : "false"; - return resultMap; + return resultMap; } function updateOption(req: Request) { - const {name, value} = req.params; + const { name, value } = req.params; - if (!update(name, value)) { - throw new ValidationError("not allowed option to change"); - } + if (!update(name, value)) { + throw new ValidationError("not allowed option to change"); + } } function updateOptions(req: Request) { - for (const optionName in req.body) { - console.log( optionName ) - if (!update(optionName, req.body[optionName])) { - // this should be improved - // it should return 400 instead of current 500, but at least it now rollbacks transaction - throw new Error(`Option '${optionName}' is not allowed to be changed`); - } + for (const optionName in req.body) { + console.log(optionName); + if (!update(optionName, req.body[optionName])) { + // this should be improved + // it should return 400 instead of current 500, but at least it now rollbacks transaction + throw new Error(`Option '${optionName}' is not allowed to be changed`); } + } } function update(name: string, value: string) { - if (!isAllowed(name)) { - return false; - } + if (!isAllowed(name)) { + return false; + } - if (name !== 'openNoteContexts') { - log.info(`Updating option '${name}' to '${value}'`); - } + if (name !== "openNoteContexts") { + log.info(`Updating option '${name}' to '${value}'`); + } - optionService.setOption(name, value); + optionService.setOption(name, value); - return true; + return true; } function getUserThemes() { - const notes = searchService.searchNotes("#appTheme", {ignoreHoistedNote: true}); - const ret = []; + const notes = searchService.searchNotes("#appTheme", { + ignoreHoistedNote: true, + }); + const ret = []; - for (const note of notes) { - let value = note.getOwnedLabelValue('appTheme'); + for (const note of notes) { + let value = note.getOwnedLabelValue("appTheme"); - if (!value) { - value = note.title.toLowerCase().replace(/[^a-z0-9]/gi, '-'); - } - - ret.push({ - val: value, - title: note.title, - noteId: note.noteId - }); + if (!value) { + value = note.title.toLowerCase().replace(/[^a-z0-9]/gi, "-"); } - return ret; + ret.push({ + val: value, + title: note.title, + noteId: note.noteId, + }); + } + + return ret; } function isAllowed(name: string) { - return ALLOWED_OPTIONS.has(name) - || name.startsWith("keyboardShortcuts") - || name.endsWith("Collapsed") - || name.startsWith("hideArchivedNotes"); + return ( + ALLOWED_OPTIONS.has(name) || + name.startsWith("keyboardShortcuts") || + name.endsWith("Collapsed") || + name.startsWith("hideArchivedNotes") + ); } export = { - getOptions, - updateOption, - updateOptions, - getUserThemes + getOptions, + updateOption, + updateOptions, + getUserThemes, }; diff --git a/src/routes/api/totp.ts b/src/routes/api/totp.ts index a4297c130..020e7e55c 100644 --- a/src/routes/api/totp.ts +++ b/src/routes/api/totp.ts @@ -1,4 +1,6 @@ import options = require("../../services/options"); +import totp_secret = require("../../services/encryption/totp_secret"); +import { Request } from "express"; const speakeasy = require("speakeasy"); function verifyOTPToken(guessedToken: any) { @@ -20,24 +22,35 @@ function generateSecret() { } function checkForTOTP() { - const totpEnabled = options.getOptionBool("totpEnabled") + const totpEnabled = options.getOptionBool("totpEnabled"); return { success: "true", message: totpEnabled }; } function enableTOTP() { - options.setOption("totpEnabled", true) + options.setOption("totpEnab| voidled", true); return { success: "true" }; } function disableTOTP() { - options.setOption("totpEnabled", false) + options.setOption("totpEnabled", false); return { success: "true" }; } +function setTotpSecret(req: Request) { + console.log("TODO: Save Secret"); + // totp_secret.setTotpSecret(req.body.secret); +} + +function getSecret() { + return "TODO: Get Secret"; +} + export = { verifyOTPToken, generateSecret, checkForTOTP, enableTOTP, - disableTOTP + disableTOTP, + setTotpSecret, + getSecret, }; diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 6f1eb3f1d..3b3c60225 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -157,8 +157,10 @@ function register(app: express.Application) { apiRoute(GET, "/api/totp/generate", totp.generateSecret); apiRoute(GET, "/api/totp/enabled", totp.checkForTOTP); - apiRoute(GET, "/api/totp/enable", totp.enableTOTP); - apiRoute(GET, "/api/totp/disable", totp.disableTOTP); + apiRoute(PST, "/api/totp/enable", totp.enableTOTP); + apiRoute(PST, "/api/totp/disable", totp.disableTOTP); + apiRoute(PST, "/api/totp/set", totp.setTotpSecret); + apiRoute(GET, "/api/totp/get", totp.getSecret); apiRoute(GET, "/api/tree", treeApiRoute.getTree); apiRoute(PST, "/api/tree/load", treeApiRoute.load); @@ -185,7 +187,7 @@ function register(app: express.Application) { apiRoute(PUT, "/api/notes/:noteId/title", notesApiRoute.changeTitle); apiRoute( PST, - "/api/notes/:noteId/duplicate/:parentNoteId", + "/api/notes/:noteId/duplicPOSTate/:parentNoteId", notesApiRoute.duplicateSubtree ); apiRoute(