mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	refactor: 💡 refact recovery code
This commit is contained in:
		| @@ -126,9 +126,9 @@ interface TOTPStatus { | ||||
|  | ||||
| interface RecoveryKeysResponse { | ||||
|     success: boolean; | ||||
|     recoveryCodes?: string; | ||||
|     recoveryCodes?: string[]; | ||||
|     keysExist?: boolean; | ||||
|     usedRecoveryCodes?: string; | ||||
|     usedRecoveryCodes?: string[]; | ||||
| } | ||||
|  | ||||
| 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"); | ||||
|  | ||||
|         if (usedResult.usedRecoveryCodes) { | ||||
|             this.keyFiller(usedResult.usedRecoveryCodes); | ||||
|             this.$generateRecoveryCodeButton.text(t("multi_factor_authentication.recovery_keys_regenerate")); | ||||
| @@ -239,14 +240,19 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private keyFiller(values: string) { | ||||
|         const keys = values.split(',').slice(0, 8); | ||||
|  | ||||
|     private keyFiller(values: string[]) { | ||||
|         this.fillKeys(""); | ||||
|  | ||||
|         keys.forEach((key, index) => { | ||||
|             if (index < 8 && key && typeof key === 'string') { | ||||
|                 this.$recoveryKeys[index].text(key.trim()); | ||||
|         values.forEach((key, index) => { | ||||
|             if (typeof key === 'string') { | ||||
|                 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_generate": "生成恢复代码", | ||||
|     "recovery_keys_regenerate": "重新生成恢复代码", | ||||
|     "recovery_keys_used": "已使用: {{date}}", | ||||
|     "recovery_keys_unused": "恢复代码 {{index}} 未使用", | ||||
|     "oauth_title": "OAuth/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 和客户端密钥,并重新启动应用程序。", | ||||
|   | ||||
| @@ -1333,6 +1333,8 @@ | ||||
|     "recovery_keys_no_key_set": "No recovery codes set", | ||||
|     "recovery_keys_generate": "Generate 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_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.", | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import type { Request } from 'express'; | ||||
| import { randomBytes } from 'crypto'; | ||||
|  | ||||
| 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!' }; | ||||
| } | ||||
|  | ||||
| @@ -31,15 +31,28 @@ function generateRecoveryCodes() { | ||||
|         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() { | ||||
|     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 { | ||||
|         success: true, | ||||
|         usedRecoveryCodes: recovery_codes.getUsedRecoveryCodes().toString() | ||||
|         usedRecoveryCodes: usedStatus | ||||
|     }; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| import sql from '../sql.js'; | ||||
| import optionService from '../options.js'; | ||||
| import crypto from 'crypto'; | ||||
| @@ -26,7 +24,7 @@ function setRecoveryCodes(recoveryCodes: string) { | ||||
|  | ||||
| function getRecoveryCodes() { | ||||
|     if (!isRecoveryCodeSet()) { | ||||
|         return Array(8).fill("Keys not set") | ||||
|         return [] | ||||
|     } | ||||
|  | ||||
|     return sql.transactional(() => { | ||||
| @@ -67,25 +65,9 @@ function verifyRecoveryCode(recoveryCodeGuess: string) { | ||||
|     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 { | ||||
|     setRecoveryCodes, | ||||
|     getRecoveryCodes, | ||||
|     verifyRecoveryCode, | ||||
|     getUsedRecoveryCodes, | ||||
|     isRecoveryCodeSet | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user