mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	refactor: 💡 refact recovery code
This commit is contained in:
		| @@ -126,9 +126,9 @@ interface TOTPStatus { | |||||||
|  |  | ||||||
| interface RecoveryKeysResponse { | interface RecoveryKeysResponse { | ||||||
|     success: boolean; |     success: boolean; | ||||||
|     recoveryCodes?: string; |     recoveryCodes?: string[]; | ||||||
|     keysExist?: boolean; |     keysExist?: boolean; | ||||||
|     usedRecoveryCodes?: string; |     usedRecoveryCodes?: string[]; | ||||||
| } | } | ||||||
|  |  | ||||||
| export default class MultiFactorAuthenticationOptions extends OptionsWidget { | export default class MultiFactorAuthenticationOptions extends OptionsWidget { | ||||||
| @@ -231,6 +231,7 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         const usedResult = await server.get<RecoveryKeysResponse>("totp_recovery/used"); |         const usedResult = await server.get<RecoveryKeysResponse>("totp_recovery/used"); | ||||||
|  |  | ||||||
|         if (usedResult.usedRecoveryCodes) { |         if (usedResult.usedRecoveryCodes) { | ||||||
|             this.keyFiller(usedResult.usedRecoveryCodes); |             this.keyFiller(usedResult.usedRecoveryCodes); | ||||||
|             this.$generateRecoveryCodeButton.text(t("multi_factor_authentication.recovery_keys_regenerate")); |             this.$generateRecoveryCodeButton.text(t("multi_factor_authentication.recovery_keys_regenerate")); | ||||||
| @@ -239,14 +240,19 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private keyFiller(values: string) { |     private keyFiller(values: string[]) { | ||||||
|         const keys = values.split(',').slice(0, 8); |  | ||||||
|  |  | ||||||
|         this.fillKeys(""); |         this.fillKeys(""); | ||||||
|  |  | ||||||
|         keys.forEach((key, index) => { |         values.forEach((key, index) => { | ||||||
|             if (index < 8 && key && typeof key === 'string') { |             if (typeof key === 'string') { | ||||||
|                 this.$recoveryKeys[index].text(key.trim()); |                 const date = new Date(key.replace(/\//g, '-')); | ||||||
|  |                 if (isNaN(date.getTime())) { | ||||||
|  |                     this.$recoveryKeys[index].text(key); | ||||||
|  |                 } else { | ||||||
|  |                     this.$recoveryKeys[index].text(t("multi_factor_authentication.recovery_keys_used", { date: key.replace(/\//g, '-') })); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 this.$recoveryKeys[index].text(t("multi_factor_authentication.recovery_keys_unused", { index: key })); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1322,6 +1322,8 @@ | |||||||
|     "recovery_keys_no_key_set": "未设置恢复代码", |     "recovery_keys_no_key_set": "未设置恢复代码", | ||||||
|     "recovery_keys_generate": "生成恢复代码", |     "recovery_keys_generate": "生成恢复代码", | ||||||
|     "recovery_keys_regenerate": "重新生成恢复代码", |     "recovery_keys_regenerate": "重新生成恢复代码", | ||||||
|  |     "recovery_keys_used": "已使用: {{date}}", | ||||||
|  |     "recovery_keys_unused": "恢复代码 {{index}} 未使用", | ||||||
|     "oauth_title": "OAuth/OpenID 认证", |     "oauth_title": "OAuth/OpenID 认证", | ||||||
|     "oauth_description": "OpenID 是一种标准化方式,允许您使用其他服务(如 Google)的账户登录网站,以验证您的身份。请参阅这些 <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">指南</a> 通过 Google 设置 OpenID 服务。", |     "oauth_description": "OpenID 是一种标准化方式,允许您使用其他服务(如 Google)的账户登录网站,以验证您的身份。请参阅这些 <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">指南</a> 通过 Google 设置 OpenID 服务。", | ||||||
|     "oauth_description_warning": "要启用 OAuth/OpenID,您需要设置 config.ini 文件中的 OAuth/OpenID 基础 URL、客户端 ID 和客户端密钥,并重新启动应用程序。", |     "oauth_description_warning": "要启用 OAuth/OpenID,您需要设置 config.ini 文件中的 OAuth/OpenID 基础 URL、客户端 ID 和客户端密钥,并重新启动应用程序。", | ||||||
|   | |||||||
| @@ -1333,6 +1333,8 @@ | |||||||
|     "recovery_keys_no_key_set": "No recovery codes set", |     "recovery_keys_no_key_set": "No recovery codes set", | ||||||
|     "recovery_keys_generate": "Generate Recovery Codes", |     "recovery_keys_generate": "Generate Recovery Codes", | ||||||
|     "recovery_keys_regenerate": "Regenerate Recovery Codes", |     "recovery_keys_regenerate": "Regenerate Recovery Codes", | ||||||
|  |     "recovery_keys_used": "Used: {{date}}", | ||||||
|  |     "recovery_keys_unused": "Recovery code {{index}} is unused", | ||||||
|     "oauth_title": "OAuth/OpenID", |     "oauth_title": "OAuth/OpenID", | ||||||
|     "oauth_description": "OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity. Follow these <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">instructions</a> to setup an OpenID service through Google.", |     "oauth_description": "OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity. Follow these <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">instructions</a> to setup an OpenID service through Google.", | ||||||
|     "oauth_description_warning": "To enable OAuth/OpenID, you need to set the OAuth/OpenID base URL, client ID and client secret in the config.ini file and restart the application.", |     "oauth_description_warning": "To enable OAuth/OpenID, you need to set the OAuth/OpenID base URL, client ID and client secret in the config.ini file and restart the application.", | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ import type { Request } from 'express'; | |||||||
| import { randomBytes } from 'crypto'; | import { randomBytes } from 'crypto'; | ||||||
|  |  | ||||||
| function setRecoveryCodes(req: Request) { | function setRecoveryCodes(req: Request) { | ||||||
|     const success = recovery_codes.setRecoveryCodes(req.body.recoveryCodes); |     const success = recovery_codes.setRecoveryCodes(req.body.recoveryCodes.join(',')); | ||||||
|     return { success: success, message: 'Recovery codes set!' }; |     return { success: success, message: 'Recovery codes set!' }; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -31,15 +31,28 @@ function generateRecoveryCodes() { | |||||||
|         randomBytes(16).toString('base64') |         randomBytes(16).toString('base64') | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|     recovery_codes.setRecoveryCodes(recoveryKeys.toString()); |     recovery_codes.setRecoveryCodes(recoveryKeys.join(',')); | ||||||
|  |  | ||||||
|     return { success: true, recoveryCodes: recoveryKeys.toString() }; |     return { success: true, recoveryCodes: recoveryKeys }; | ||||||
| } | } | ||||||
|  |  | ||||||
| function getUsedRecoveryCodes() { | function getUsedRecoveryCodes() { | ||||||
|  |     if (!recovery_codes.isRecoveryCodeSet()) { | ||||||
|  |         return [] | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const dateRegex = RegExp(/^\d{4}\/\d{2}\/\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/gm); | ||||||
|  |     const recoveryCodes = recovery_codes.getRecoveryCodes(); | ||||||
|  |     const usedStatus: string[] = []; | ||||||
|  |  | ||||||
|  |     recoveryCodes.forEach((recoveryKey: string) => { | ||||||
|  |         if (dateRegex.test(recoveryKey)) usedStatus.push(recoveryKey); | ||||||
|  |         else usedStatus.push(recoveryCodes.indexOf(recoveryKey)); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         success: true, |         success: true, | ||||||
|         usedRecoveryCodes: recovery_codes.getUsedRecoveryCodes().toString() |         usedRecoveryCodes: usedStatus | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| 'use strict'; |  | ||||||
|  |  | ||||||
| import sql from '../sql.js'; | import sql from '../sql.js'; | ||||||
| import optionService from '../options.js'; | import optionService from '../options.js'; | ||||||
| import crypto from 'crypto'; | import crypto from 'crypto'; | ||||||
| @@ -26,7 +24,7 @@ function setRecoveryCodes(recoveryCodes: string) { | |||||||
|  |  | ||||||
| function getRecoveryCodes() { | function getRecoveryCodes() { | ||||||
|     if (!isRecoveryCodeSet()) { |     if (!isRecoveryCodeSet()) { | ||||||
|         return Array(8).fill("Keys not set") |         return [] | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return sql.transactional(() => { |     return sql.transactional(() => { | ||||||
| @@ -67,25 +65,9 @@ function verifyRecoveryCode(recoveryCodeGuess: string) { | |||||||
|     return loginSuccess; |     return loginSuccess; | ||||||
| } | } | ||||||
|  |  | ||||||
| function getUsedRecoveryCodes() { |  | ||||||
|     if (!isRecoveryCodeSet()) { |  | ||||||
|         return Array(8).fill("Recovery code not set") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const dateRegex = RegExp(/^\d{4}\/\d{2}\/\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/gm); |  | ||||||
|     const recoveryCodes = getRecoveryCodes(); |  | ||||||
|     const usedStatus: string[] = []; |  | ||||||
|  |  | ||||||
|     recoveryCodes.forEach((recoveryKey: string) => { |  | ||||||
|         if (dateRegex.test(recoveryKey)) usedStatus.push('Used: ' + recoveryKey); |  | ||||||
|         else usedStatus.push('Recovery code ' + recoveryCodes.indexOf(recoveryKey) + ' is unused'); |  | ||||||
|     }); |  | ||||||
|     return usedStatus; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|     setRecoveryCodes, |     setRecoveryCodes, | ||||||
|  |     getRecoveryCodes, | ||||||
|     verifyRecoveryCode, |     verifyRecoveryCode, | ||||||
|     getUsedRecoveryCodes, |  | ||||||
|     isRecoveryCodeSet |     isRecoveryCodeSet | ||||||
| }; | }; | ||||||
		Reference in New Issue
	
	Block a user