fix(system): 公开设置不再返回存储的连接配置

/system/setting/public/{key} 只要求登录用户,白名单中的 Storages 原样返回
整个存储列表,每项的 config 保存着各存储的连接配置和凭据(SMB 用户名密码、
alist 账号、115 token 等)。设置合同已将 Storages 标记为 sensitive,
/system/settings 默认会脱敏,只有这个公开接口直接返回原值。

前端 v3 已改用 /storage/options 读取存储名称和类型,公开接口的 Storages
目前只剩 Agent 的 config.public.get 和旧版前端页面会用到,它们只需要
name、type。因此保留该 key,改为与 /storage/options 相同的投影:只返回
name(缺省时取 type)和 type,跳过没有 type 的项。所有调用者返回相同
结果,超级管理员仍可通过 /system/setting/Storages 读取完整配置。

白名单内其他配置(目录、站点范围、集数定位规则、默认订阅规则、Follow
订阅者)不含连接凭据,保持原值返回。同步更新 docs/mcp-api.md 的接口说明。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
LIN student
2026-10-05 15:02:55 +08:00
parent 4ca70b478b
commit 75d8b2e441
3 changed files with 76 additions and 3 deletions

View File

@@ -232,6 +232,65 @@ def test_system_public_setting_allows_only_non_sensitive_keys(monkeypatch):
assert exc_info.value.detail == "配置项不存在"
def test_system_public_setting_storages_returns_name_and_type_only(monkeypatch):
"""公开设置读取存储时只返回名称和类型,与 /storage/options 一致,不含连接配置。"""
storages = [
{"type": "local", "name": "本地存储", "config": {}},
{
"type": "smb",
"name": "NAS",
"config": {"host": "192.168.1.2", "username": "smb-user", "password": "smb-password"},
},
{"type": "u115", "config": {"refresh_token": "u115-refresh", "access_token": "u115-access"}},
{"name": "缺少类型", "config": {"token": "untyped-token"}},
]
fake_config = SimpleNamespace(get=lambda key: storages if key == SystemConfigKey.Storages else None)
monkeypatch.setattr(system_endpoint, "get_configured_system_config", lambda: fake_config)
monkeypatch.setattr("app.application.storage.get_configured_system_config", lambda: fake_config)
response = asyncio.run(system_endpoint.get_public_setting(SystemConfigKey.Storages.value))
assert response.success is True
assert response.data == {
"value": [
{"name": "本地存储", "type": "local"},
{"name": "NAS", "type": "smb"},
{"name": "u115", "type": "u115"},
]
}
assert response.data["value"] == [option.model_dump() for option in storage_endpoint.storage_options()]
serialized = response.model_dump_json()
for value in ("smb-user", "smb-password", "192.168.1.2", "u115-refresh", "u115-access", "untyped-token"):
assert value not in serialized
@pytest.mark.parametrize(
"config_key",
[
SystemConfigKey.Directories,
SystemConfigKey.IndexerSites,
SystemConfigKey.EpisodeFormatRuleTable,
SystemConfigKey.DefaultMovieSubscribeConfig,
SystemConfigKey.DefaultTvSubscribeConfig,
SystemConfigKey.DefaultMusicSubscribeConfig,
SystemConfigKey.FollowSubscribers,
],
)
def test_system_public_setting_returns_other_keys_as_stored(monkeypatch, config_key):
"""存储以外的公开配置仍按原值返回。"""
stored = [{"name": "配置项", "config": {"nested": True}}]
monkeypatch.setattr(
system_endpoint,
"get_configured_system_config",
lambda: SimpleNamespace(get=lambda key: stored if key == config_key else None),
)
response = asyncio.run(system_endpoint.get_public_setting(config_key.value))
assert response.success is True
assert response.data == {"value": stored}
def test_system_ping_returns_success():
"""服务存活检测接口返回标准成功响应。"""
response = asyncio.run(system_endpoint.ping())