From 90b79d53fa4604e86e40dab97fe676e014b0d92a Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Fri, 3 Apr 2026 17:37:03 +0800 Subject: [PATCH 1/7] fix(webui): enforce 12-char dashboard password policy with backend+frontend validation --- astrbot/cli/commands/cmd_conf.py | 14 ++- astrbot/core/config/astrbot_config.py | 10 ++ astrbot/core/config/default.py | 2 +- astrbot/core/utils/auth_password.py | 113 ++++++++++++++++++ astrbot/dashboard/routes/auth.py | 46 ++++++- astrbot/dashboard/routes/stat.py | 13 +- .../src/i18n/locales/en-US/core/header.json | 9 +- .../src/i18n/locales/en-US/features/auth.json | 4 +- .../locales/en-US/messages/validation.json | 4 +- .../src/i18n/locales/ru-RU/core/header.json | 13 +- .../src/i18n/locales/ru-RU/features/auth.json | 4 +- .../locales/ru-RU/messages/validation.json | 4 +- .../src/i18n/locales/zh-CN/core/header.json | 9 +- .../src/i18n/locales/zh-CN/features/auth.json | 4 +- .../locales/zh-CN/messages/validation.json | 4 +- .../full/vertical-header/VerticalHeader.vue | 61 ++++++++-- dashboard/src/stores/auth.ts | 14 ++- .../authentication/authForms/AuthLogin.vue | 10 +- 18 files changed, 276 insertions(+), 62 deletions(-) create mode 100644 astrbot/core/utils/auth_password.py diff --git a/astrbot/cli/commands/cmd_conf.py b/astrbot/cli/commands/cmd_conf.py index 5a39cb2f7e..ca7cd3778d 100644 --- a/astrbot/cli/commands/cmd_conf.py +++ b/astrbot/cli/commands/cmd_conf.py @@ -1,4 +1,3 @@ -import hashlib import json import zoneinfo from collections.abc import Callable @@ -6,6 +5,11 @@ import click +from astrbot.core.utils.auth_password import ( + hash_dashboard_password, + validate_dashboard_password, +) + from ..utils import check_astrbot_root, get_astrbot_root @@ -39,9 +43,11 @@ def _validate_dashboard_username(value: str) -> str: def _validate_dashboard_password(value: str) -> str: """Validate Dashboard password""" - if not value: - raise click.ClickException("Password cannot be empty") - return hashlib.md5(value.encode()).hexdigest() + try: + validate_dashboard_password(value) + except ValueError as e: + raise click.ClickException(str(e)) + return hash_dashboard_password(value) def _validate_timezone(value: str) -> str: diff --git a/astrbot/core/config/astrbot_config.py b/astrbot/core/config/astrbot_config.py index 77c298cac8..c6bbb28b9f 100644 --- a/astrbot/core/config/astrbot_config.py +++ b/astrbot/core/config/astrbot_config.py @@ -4,6 +4,9 @@ import os from astrbot.core.utils.astrbot_path import get_astrbot_data_path +from astrbot.core.utils.auth_password import ( + normalize_dashboard_password_hash, +) from .default import DEFAULT_CONFIG, DEFAULT_VALUE_MAP @@ -59,6 +62,13 @@ def __init__( # 检查配置完整性,并插入 has_new = self.check_config_integrity(default_config, conf) + if ( + "dashboard" in conf + and isinstance(conf["dashboard"], dict) + and not conf["dashboard"].get("password") + ): + conf["dashboard"]["password"] = normalize_dashboard_password_hash("") + has_new = True self.update(conf) if has_new: self.save_config() diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 45412bdccb..9e41032f94 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -235,7 +235,7 @@ "dashboard": { "enable": True, "username": "astrbot", - "password": "77b90590a8945a7d36c963981a307dc9", + "password": "", "jwt_secret": "", "host": "0.0.0.0", "port": 6185, diff --git a/astrbot/core/utils/auth_password.py b/astrbot/core/utils/auth_password.py new file mode 100644 index 0000000000..3d1917c98f --- /dev/null +++ b/astrbot/core/utils/auth_password.py @@ -0,0 +1,113 @@ +"""Utilities for dashboard password hashing and verification.""" + +import hashlib +import hmac +import re +import secrets + +_PBKDF2_ITERATIONS = 200_000 +_PBKDF2_SALT_BYTES = 16 +_PBKDF2_ALGORITHM = "pbkdf2_sha256" +_PBKDF2_FORMAT = f"{_PBKDF2_ALGORITHM}$" +_LEGACY_MD5_LENGTH = 32 +_DASHBOARD_PASSWORD_MIN_LENGTH = 12 +DEFAULT_DASHBOARD_PASSWORD = "astrbot" + + +def hash_dashboard_password(raw_password: str) -> str: + """Return a salted hash for dashboard password using PBKDF2-HMAC-SHA256.""" + if not isinstance(raw_password, str) or raw_password == "": + raise ValueError("Password cannot be empty") + + salt = secrets.token_hex(_PBKDF2_SALT_BYTES) + digest = hashlib.pbkdf2_hmac( + "sha256", + raw_password.encode("utf-8"), + bytes.fromhex(salt), + _PBKDF2_ITERATIONS, + ).hex() + return f"{_PBKDF2_FORMAT}{_PBKDF2_ITERATIONS}${salt}${digest}" + + +def validate_dashboard_password(raw_password: str) -> None: + """Validate whether dashboard password meets the minimal complexity policy.""" + if not isinstance(raw_password, str) or raw_password == "": + raise ValueError("Password cannot be empty") + if len(raw_password) < _DASHBOARD_PASSWORD_MIN_LENGTH: + raise ValueError( + f"Password must be at least {_DASHBOARD_PASSWORD_MIN_LENGTH} characters long" + ) + + if not re.search(r"[A-Z]", raw_password): + raise ValueError("Password must include at least one uppercase letter") + if not re.search(r"[a-z]", raw_password): + raise ValueError("Password must include at least one lowercase letter") + if not re.search(r"\d", raw_password): + raise ValueError("Password must include at least one digit") + + +def normalize_dashboard_password_hash(stored_password: str) -> str: + """Ensure dashboard password has a value, fallback to default dashboard password hash.""" + if not stored_password: + return hash_dashboard_password(DEFAULT_DASHBOARD_PASSWORD) + return stored_password + + +def _is_legacy_md5_hash(stored: str) -> bool: + return ( + isinstance(stored, str) + and len(stored) == _LEGACY_MD5_LENGTH + and all(c in "0123456789abcdefABCDEF" for c in stored) + ) + + +def _is_pbkdf2_hash(stored: str) -> bool: + return isinstance(stored, str) and stored.startswith(_PBKDF2_FORMAT) + + +def verify_dashboard_password(stored_hash: str, candidate_password: str) -> bool: + """Verify password against legacy md5 or new PBKDF2-SHA256 format.""" + if not isinstance(stored_hash, str) or not isinstance(candidate_password, str): + return False + + if _is_legacy_md5_hash(stored_hash): + # Keep compatibility with existing md5-based deployments: + # new clients send plain password, old clients may send md5 of it. + candidate_md5 = hashlib.md5(candidate_password.encode("utf-8")).hexdigest() + return hmac.compare_digest( + stored_hash.lower(), candidate_md5.lower() + ) or hmac.compare_digest( + stored_hash.lower(), + candidate_password.lower(), + ) + + if _is_pbkdf2_hash(stored_hash): + parts: list[str] = stored_hash.split("$") + if len(parts) != 4: + return False + _, iterations_s, salt, digest = parts + try: + iterations = int(iterations_s) + stored_key = bytes.fromhex(digest) + salt_bytes = bytes.fromhex(salt) + except (TypeError, ValueError): + return False + candidate_key = hashlib.pbkdf2_hmac( + "sha256", + candidate_password.encode("utf-8"), + salt_bytes, + iterations, + ) + return hmac.compare_digest(stored_key, candidate_key) + + return False + + +def is_default_dashboard_password(stored_hash: str) -> bool: + """Check whether the password still equals the built-in default value.""" + return verify_dashboard_password(stored_hash, DEFAULT_DASHBOARD_PASSWORD) + + +def is_legacy_dashboard_password(stored_hash: str) -> bool: + """Check whether the password is still stored with legacy MD5.""" + return _is_legacy_md5_hash(stored_hash) diff --git a/astrbot/dashboard/routes/auth.py b/astrbot/dashboard/routes/auth.py index eac5f65b0b..6d1d43b977 100644 --- a/astrbot/dashboard/routes/auth.py +++ b/astrbot/dashboard/routes/auth.py @@ -6,6 +6,13 @@ from astrbot import logger from astrbot.core import DEMO_MODE +from astrbot.core.utils.auth_password import ( + hash_dashboard_password, + is_default_dashboard_password, + is_legacy_dashboard_password, + validate_dashboard_password, + verify_dashboard_password, +) from .route import Response, Route, RouteContext @@ -23,15 +30,29 @@ async def login(self): username = self.config["dashboard"]["username"] password = self.config["dashboard"]["password"] post_data = await request.json - if post_data["username"] == username and post_data["password"] == password: + + req_username = ( + post_data.get("username") if isinstance(post_data, dict) else None + ) + req_password = ( + post_data.get("password") if isinstance(post_data, dict) else None + ) + if not isinstance(req_username, str) or not isinstance(req_password, str): + return Response().error("Invalid request payload").__dict__ + + if req_username == username and verify_dashboard_password( + password, req_password + ): change_pwd_hint = False + legacy_pwd_hint = is_legacy_dashboard_password(password) if ( username == "astrbot" - and password == "77b90590a8945a7d36c963981a307dc9" + and is_default_dashboard_password(password) and not DEMO_MODE ): change_pwd_hint = True - logger.warning("为了保证安全,请尽快修改默认密码。") + logger.warning("检测到默认管理员凭据,请尽快修改密码。") + legacy_pwd_hint = True return ( Response() @@ -40,6 +61,7 @@ async def login(self): "token": self.generate_jwt(username), "username": username, "change_pwd_hint": change_pwd_hint, + "legacy_pwd_hint": legacy_pwd_hint, }, ) .__dict__ @@ -57,8 +79,14 @@ async def edit_account(self): password = self.config["dashboard"]["password"] post_data = await request.json + if not isinstance(post_data, dict): + return Response().error("Invalid request payload").__dict__ + + req_password = post_data.get("password") + if not isinstance(req_password, str): + return Response().error("Invalid request payload").__dict__ - if post_data["password"] != password: + if not verify_dashboard_password(password, req_password): return Response().error("原密码错误").__dict__ new_pwd = post_data.get("new_password", None) @@ -68,10 +96,16 @@ async def edit_account(self): # Verify password confirmation if new_pwd: + if not isinstance(new_pwd, str): + return Response().error("新密码无效").__dict__ confirm_pwd = post_data.get("confirm_password", None) - if confirm_pwd != new_pwd: + if not isinstance(confirm_pwd, str) or confirm_pwd != new_pwd: return Response().error("两次输入的新密码不一致").__dict__ - self.config["dashboard"]["password"] = new_pwd + try: + validate_dashboard_password(new_pwd) + except ValueError as e: + return Response().error(str(e)).__dict__ + self.config["dashboard"]["password"] = hash_dashboard_password(new_pwd) if new_username: self.config["dashboard"]["username"] = new_username diff --git a/astrbot/dashboard/routes/stat.py b/astrbot/dashboard/routes/stat.py index 2eb3cd400e..efeb0631e3 100644 --- a/astrbot/dashboard/routes/stat.py +++ b/astrbot/dashboard/routes/stat.py @@ -12,7 +12,7 @@ import aiohttp import psutil from quart import request -from sqlmodel import select +from sqlmodel import col, select from astrbot.core import DEMO_MODE, logger from astrbot.core.config import VERSION @@ -21,6 +21,10 @@ from astrbot.core.db.migration.helper import check_migration_needed_v4 from astrbot.core.db.po import ProviderStat from astrbot.core.utils.astrbot_path import get_astrbot_path +from astrbot.core.utils.auth_password import ( + is_default_dashboard_password, + is_legacy_dashboard_password, +) from astrbot.core.utils.io import get_dashboard_version from astrbot.core.utils.storage_cleaner import StorageCleaner from astrbot.core.utils.version_comparator import VersionComparator @@ -76,7 +80,7 @@ def is_default_cred(self): password = self.config["dashboard"]["password"] return ( username == "astrbot" - and password == "77b90590a8945a7d36c963981a307dc9" + and is_default_dashboard_password(password) and not DEMO_MODE ) @@ -90,6 +94,9 @@ async def get_version(self): "version": VERSION, "dashboard_version": await get_dashboard_version(), "change_pwd_hint": self.is_default_cred(), + "legacy_pwd_hint": is_legacy_dashboard_password( + self.config["dashboard"]["password"] + ), "need_migration": need_migration, }, ) @@ -226,7 +233,7 @@ async def get_provider_token_stats(self): ProviderStat.agent_type == "internal", ProviderStat.created_at >= query_start_utc, ) - .order_by(ProviderStat.created_at.asc()) + .order_by(col(ProviderStat.created_at).asc()) ) records = result.scalars().all() diff --git a/dashboard/src/i18n/locales/en-US/core/header.json b/dashboard/src/i18n/locales/en-US/core/header.json index 4da98b8dd8..02645c23ee 100644 --- a/dashboard/src/i18n/locales/en-US/core/header.json +++ b/dashboard/src/i18n/locales/en-US/core/header.json @@ -80,20 +80,21 @@ }, "accountDialog": { "title": "Modify Account", - "securityWarning": "Security Reminder: Please change the default password to ensure account security", + "securityWarning": "Security Reminder: Please change your password to secure your account", + "securityWarningLegacy": "The new AstrBot version has improved security. Please change your password.", "form": { "currentPassword": "Current Password", "newPassword": "New Password", "confirmPassword": "Confirm New Password", "newUsername": "New Username (Optional)", - "passwordHint": "Password must be at least 8 characters", + "passwordHint": "Password must be at least 12 characters", "confirmPasswordHint": "Please enter new password again to confirm", "usernameHint": "Leave blank to keep current username", - "defaultCredentials": "Default username and password are both astrbot" + "defaultCredentials": "The new AstrBot version has improved security. Please change your password." }, "validation": { "passwordRequired": "Please enter password", - "passwordMinLength": "Password must be at least 8 characters", + "passwordMinLength": "Password must be at least 12 characters", "passwordMatch": "Passwords do not match", "usernameMinLength": "Username must be at least 3 characters" }, diff --git a/dashboard/src/i18n/locales/en-US/features/auth.json b/dashboard/src/i18n/locales/en-US/features/auth.json index 5c44558a03..b8dee2b803 100644 --- a/dashboard/src/i18n/locales/en-US/features/auth.json +++ b/dashboard/src/i18n/locales/en-US/features/auth.json @@ -2,7 +2,7 @@ "login": "Login", "username": "Username", "password": "Password", - "defaultHint": "Default username and password: astrbot", + "defaultHint": "The new AstrBot version has improved security. Please change your password.", "logo": { "title": "AstrBot Dashboard", "subtitle": "Welcome" @@ -11,4 +11,4 @@ "switchToDark": "Switch to Dark Theme", "switchToLight": "Switch to Light Theme" } -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/en-US/messages/validation.json b/dashboard/src/i18n/locales/en-US/messages/validation.json index 407f638c57..1b34f5b5f1 100644 --- a/dashboard/src/i18n/locales/en-US/messages/validation.json +++ b/dashboard/src/i18n/locales/en-US/messages/validation.json @@ -14,7 +14,7 @@ "fileType": "Unsupported file type", "required_field": "Please fill in the required field", "invalid_format": "Invalid format", - "password_too_short": "Password must be at least 8 characters", + "password_too_short": "Password must be at least 12 characters", "password_too_weak": "Password is too weak", "invalid_phone": "Please enter a valid phone number", "invalid_date": "Please enter a valid date", @@ -22,4 +22,4 @@ "upload_failed": "File upload failed", "network_error": "Network connection error, please try again", "operation_cannot_be_undone": "⚠️ This operation cannot be undone, please choose carefully!" -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/ru-RU/core/header.json b/dashboard/src/i18n/locales/ru-RU/core/header.json index a23f3dfd33..38bf45bfe6 100644 --- a/dashboard/src/i18n/locales/ru-RU/core/header.json +++ b/dashboard/src/i18n/locales/ru-RU/core/header.json @@ -1,4 +1,4 @@ -{ +{ "logoTitle": "Панель управления AstrBot", "version": { "hasNewVersion": "Доступна новая версия AstrBot!", @@ -80,20 +80,21 @@ }, "accountDialog": { "title": "Изменить аккаунт", - "securityWarning": "Безопасность: Пожалуйста, смените пароль по умолчанию для защиты аккаунта", + "securityWarning": "Безопасность: Пожалуйста, смените пароль для защиты аккаунта", + "securityWarningLegacy": "Новая версия AstrBot улучшила безопасность. Пожалуйста, измените пароль.", "form": { "currentPassword": "Текущий пароль", "newPassword": "Новый пароль", "confirmPassword": "Подтвердите новый пароль", "newUsername": "Новое имя пользователя (опционально)", - "passwordHint": "Пароль должен быть не менее 8 символов", + "passwordHint": "Пароль должен быть не менее 12 символов", "confirmPasswordHint": "Введите новый пароль еще раз", "usernameHint": "Оставьте пустым, если не хотите менять имя пользователя", - "defaultCredentials": "Логин и пароль по умолчанию: astrbot" + "defaultCredentials": "Новая версия AstrBot улучшила безопасность. Пожалуйста, измените пароль." }, "validation": { "passwordRequired": "Введите пароль", - "passwordMinLength": "Пароль должен быть не менее 8 символов", + "passwordMinLength": "Пароль должен быть не менее 12 символов", "passwordMatch": "Паролы не совпадают", "usernameMinLength": "Имя пользователя должно быть не менее 3 символов" }, @@ -105,4 +106,4 @@ "updateFailed": "Ошибка обновления, попробуйте еще раз" } } -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/ru-RU/features/auth.json b/dashboard/src/i18n/locales/ru-RU/features/auth.json index d6ba05dc3b..e3f167edc3 100644 --- a/dashboard/src/i18n/locales/ru-RU/features/auth.json +++ b/dashboard/src/i18n/locales/ru-RU/features/auth.json @@ -2,7 +2,7 @@ "login": "Вход", "username": "Имя пользователя", "password": "Пароль", - "defaultHint": "Логин и пароль по умолчанию: astrbot", + "defaultHint": "Новая версия AstrBot улучшила безопасность. Пожалуйста, измените пароль.", "logo": { "title": "Панель управления AstrBot", "subtitle": "Добро пожаловать" @@ -11,4 +11,4 @@ "switchToDark": "Перейти на темную тему", "switchToLight": "Перейти на светлую тему" } -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/ru-RU/messages/validation.json b/dashboard/src/i18n/locales/ru-RU/messages/validation.json index 07d0615796..800e541fd9 100644 --- a/dashboard/src/i18n/locales/ru-RU/messages/validation.json +++ b/dashboard/src/i18n/locales/ru-RU/messages/validation.json @@ -14,7 +14,7 @@ "fileType": "Неподдерживаемый тип файла", "required_field": "Заполните обязательные поля", "invalid_format": "Некорректный формат", - "password_too_short": "Пароль должен быть не менее 8 символов", + "password_too_short": "Пароль должен быть не менее 12 символов", "password_too_weak": "Пароль слишком слабый", "invalid_phone": "Некорректный номер телефона", "invalid_date": "Некорректная дата", @@ -22,4 +22,4 @@ "upload_failed": "Загрузка не удалась", "network_error": "Ошибка сети, попробуйте снова", "operation_cannot_be_undone": "⚠️ Это действие нельзя отменить, будьте осторожны!" -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/zh-CN/core/header.json b/dashboard/src/i18n/locales/zh-CN/core/header.json index 3bb9850331..dc00ef5e4a 100644 --- a/dashboard/src/i18n/locales/zh-CN/core/header.json +++ b/dashboard/src/i18n/locales/zh-CN/core/header.json @@ -80,20 +80,21 @@ }, "accountDialog": { "title": "修改账户", - "securityWarning": "安全提醒: 请修改默认密码以确保账户安全", + "securityWarning": "安全提醒: 请修改密码以确保账户安全", + "securityWarningLegacy": "新的 AstrBot 版本强化了安全策略,请重新修改密码", "form": { "currentPassword": "当前密码", "newPassword": "新密码", "confirmPassword": "确认新密码", "newUsername": "新用户名 (可选)", - "passwordHint": "密码长度至少 8 位", + "passwordHint": "密码长度至少 12 位", "confirmPasswordHint": "请再次输入新密码以确认", "usernameHint": "留空表示不修改用户名", - "defaultCredentials": "默认用户名和密码均为 astrbot" + "defaultCredentials": "默认用户名和密码为 astrbot,请在登录后立即修改以确保安全。" }, "validation": { "passwordRequired": "请输入密码", - "passwordMinLength": "密码长度至少 8 位", + "passwordMinLength": "密码长度至少 12 位", "passwordMatch": "两次输入的密码不一致", "usernameMinLength": "用户名长度至少3位" }, diff --git a/dashboard/src/i18n/locales/zh-CN/features/auth.json b/dashboard/src/i18n/locales/zh-CN/features/auth.json index d6da999430..b927d9a0d9 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/auth.json +++ b/dashboard/src/i18n/locales/zh-CN/features/auth.json @@ -2,7 +2,7 @@ "login": "登录", "username": "用户名", "password": "密码", - "defaultHint": "默认账户和密码均为:astrbot", + "defaultHint": "默认用户名和密码为 astrbot,请在登录后立即修改以确保安全。", "logo": { "title": "AstrBot WebUI", "subtitle": "欢迎使用" @@ -11,4 +11,4 @@ "switchToDark": "切换到深色主题", "switchToLight": "切换到浅色主题" } -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/zh-CN/messages/validation.json b/dashboard/src/i18n/locales/zh-CN/messages/validation.json index da12ae79b7..98e9df8915 100644 --- a/dashboard/src/i18n/locales/zh-CN/messages/validation.json +++ b/dashboard/src/i18n/locales/zh-CN/messages/validation.json @@ -14,7 +14,7 @@ "fileType": "不支持的文件类型", "required_field": "请填写必填字段", "invalid_format": "格式无效", - "password_too_short": "密码至少需要8个字符", + "password_too_short": "密码至少需要12个字符", "password_too_weak": "密码强度太弱", "invalid_phone": "请输入有效的手机号码", "invalid_date": "请输入有效的日期", @@ -22,4 +22,4 @@ "upload_failed": "文件上传失败", "network_error": "网络连接错误,请重试", "operation_cannot_be_undone": "⚠️ 此操作无法撤销,请谨慎选择!" -} \ No newline at end of file +} diff --git a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue index 1fb32b48a0..fab98829ac 100644 --- a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue +++ b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue @@ -3,7 +3,6 @@ import { ref, computed, watch, onMounted } from 'vue'; import { useCustomizerStore } from '@/stores/customizer'; import axios from 'axios'; import Logo from '@/components/shared/Logo.vue'; -import { md5 } from 'js-md5'; import { useAuthStore } from '@/stores/auth'; import { useCommonStore } from '@/stores/common'; import { MarkdownRender, enableKatex, enableMermaid } from 'markstream-vue'; @@ -19,6 +18,7 @@ import { useLanguageSwitcher } from '@/i18n/composables'; import type { Locale } from '@/i18n/types'; import AboutPage from '@/views/AboutPage.vue'; import { getDesktopRuntimeInfo } from '@/utils/desktopRuntime'; +import LanguageSwitcher from '@/components/shared/LanguageSwitcher.vue'; enableKatex(); enableMermaid(); @@ -31,6 +31,7 @@ const LAST_BOT_ROUTE_KEY = 'astrbot:last_bot_route'; const LAST_CHAT_ROUTE_KEY = 'astrbot:last_chat_route'; let dialog = ref(false); let accountWarning = ref(false) +let accountWarningLegacy = ref(false); let updateStatusDialog = ref(false); let aboutDialog = ref(false); const username = localStorage.getItem('user'); @@ -100,7 +101,7 @@ const releasesHeader = computed(() => [ const formValid = ref(true); const passwordRules = computed(() => [ (v: string) => !!v || t('core.header.accountDialog.validation.passwordRequired'), - (v: string) => v.length >= 8 || t('core.header.accountDialog.validation.passwordMinLength') + (v: string) => v.length >= 12 || t('core.header.accountDialog.validation.passwordMinLength') ]); const confirmPasswordRules = computed(() => [ (v: string) => !newPassword.value || !!v || t('core.header.accountDialog.validation.passwordRequired'), @@ -225,9 +226,9 @@ function accountEdit() { accountEditStatus.value.error = false; accountEditStatus.value.success = false; - const passwordHash = password.value ? md5(password.value) : ''; - const newPasswordHash = newPassword.value ? md5(newPassword.value) : ''; - const confirmPasswordHash = confirmPassword.value ? md5(confirmPassword.value) : ''; + const passwordHash = password.value ? password.value : ''; + const newPasswordHash = newPassword.value ? newPassword.value : ''; + const confirmPasswordHash = confirmPassword.value ? confirmPassword.value : ''; axios.post('/api/auth/account/edit', { password: passwordHash, @@ -271,12 +272,21 @@ function getVersion() { botCurrVersion.value = "v" + res.data.data.version; dashboardCurrentVersion.value = res.data.data?.dashboard_version; let change_pwd_hint = res.data.data?.change_pwd_hint; - if (change_pwd_hint) { + const legacy_pwd_hint = res.data.data?.legacy_pwd_hint; + if (change_pwd_hint || legacy_pwd_hint) { dialog.value = true; accountWarning.value = true; + accountWarningLegacy.value = !!legacy_pwd_hint; localStorage.setItem('change_pwd_hint', 'true'); + if (legacy_pwd_hint) { + localStorage.setItem('legacy_pwd_hint', 'true'); + } else { + localStorage.removeItem('legacy_pwd_hint'); + } } else { + accountWarningLegacy.value = false; localStorage.removeItem('change_pwd_hint'); + localStorage.removeItem('legacy_pwd_hint'); } }) .catch((err) => { @@ -284,6 +294,16 @@ function getVersion() { }); } +function initPasswordWarningFromStorage() { + const hasChangePwdHint = localStorage.getItem('change_pwd_hint') === 'true'; + const hasLegacyPwdHint = localStorage.getItem('legacy_pwd_hint') === 'true'; + if (hasChangePwdHint || hasLegacyPwdHint) { + dialog.value = true; + accountWarning.value = true; + accountWarningLegacy.value = hasLegacyPwdHint; + } +} + function checkUpdate() { updateStatus.value = t('core.header.updateDialog.status.checking'); axios.get('/api/update/check') @@ -392,6 +412,7 @@ function handleLogoClick() { getVersion(); checkUpdate(); +initPasswordWarningFromStorage(); const commonStore = useCommonStore(); commonStore.createEventSource(); // log @@ -889,14 +910,18 @@ onMounted(async () => { - +