diff --git a/astrbot/core/config/astrbot_config.py b/astrbot/core/config/astrbot_config.py index e23c5961e..4d62becb5 100644 --- a/astrbot/core/config/astrbot_config.py +++ b/astrbot/core/config/astrbot_config.py @@ -8,11 +8,13 @@ from astrbot.core.utils.auth_password import ( generate_dashboard_password, hash_dashboard_password, hash_legacy_dashboard_password, + validate_dashboard_password, ) from .default import DEFAULT_CONFIG, DEFAULT_VALUE_MAP ASTRBOT_CONFIG_PATH = os.path.join(get_astrbot_data_path(), "cmd_config.json") +DASHBOARD_INITIAL_PASSWORD_ENV = "ASTRBOT_DASHBOARD_INITIAL_PASSWORD" logger = logging.getLogger("astrbot") @@ -97,7 +99,7 @@ class AstrBotConfig(dict): self.update(conf) def _reset_generated_dashboard_password(self, conf: dict) -> None: - generated_password = generate_dashboard_password() + generated_password = self._resolve_initial_dashboard_password() conf["dashboard"]["pbkdf2_password"] = hash_dashboard_password( generated_password ) @@ -117,6 +119,14 @@ class AstrBotConfig(dict): True, ) + @staticmethod + def _resolve_initial_dashboard_password() -> str: + env_password = os.environ.get(DASHBOARD_INITIAL_PASSWORD_ENV) + if env_password is None: + return generate_dashboard_password() + validate_dashboard_password(env_password) + return env_password + def _config_schema_to_default_config(self, schema: dict) -> dict: """将 Schema 转换成 Config""" conf = {} diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index c9c7e59b4..7afe82ebe 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -229,6 +229,53 @@ class TestAstrBotConfigLoad: generated_password, ) + def test_empty_dashboard_password_uses_initial_password_env( + self, temp_config_path, monkeypatch + ): + """Test that the generated dashboard password can be provided by env.""" + env_password = "CustomInitial123" + monkeypatch.setenv("ASTRBOT_DASHBOARD_INITIAL_PASSWORD", env_password) + default_config = { + "dashboard": { + "username": "astrbot", + "password": "", + }, + } + + config = AstrBotConfig( + config_path=temp_config_path, + default_config=default_config, + ) + + assert getattr(config, "_generated_dashboard_password", None) == env_password + assert verify_dashboard_password( + config["dashboard"]["pbkdf2_password"], + env_password, + ) + assert verify_dashboard_password( + config["dashboard"]["password"], + env_password, + ) + assert config["dashboard"]["password_change_required"] is True + + def test_initial_dashboard_password_env_must_be_valid( + self, temp_config_path, monkeypatch + ): + """Test that weak env-provided initial passwords fail fast.""" + monkeypatch.setenv("ASTRBOT_DASHBOARD_INITIAL_PASSWORD", "weak") + default_config = { + "dashboard": { + "username": "astrbot", + "password": "", + }, + } + + with pytest.raises(ValueError, match="Password must be at least"): + AstrBotConfig( + config_path=temp_config_path, + default_config=default_config, + ) + def test_legacy_password_change_required_rotates_and_keeps_config_flag( self, temp_config_path ):