From 7846b24f6c4e1b63f0a551626ab44cb458f779a6 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 23 Aug 2026 01:26:09 +0800 Subject: [PATCH] refactor: inject URL signing runtime configuration --- app/application/security/url.py | 14 ++++++++++++-- docs/refactor/backend-architecture-next-stage.md | 3 ++- .../architecture/configuration-debt-baseline.json | 3 +-- .../fixtures/architecture/dependency-baseline.json | 7 ++++--- tests/test_security_utils.py | 4 ++-- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/app/application/security/url.py b/app/application/security/url.py index a7f4011d3..bef0444db 100644 --- a/app/application/security/url.py +++ b/app/application/security/url.py @@ -1,5 +1,6 @@ import asyncio import hmac +import importlib import ipaddress import socket import threading @@ -13,7 +14,7 @@ from urllib.parse import parse_qsl, quote, urlencode, urlparse, urlunparse from anyio import Path as AsyncPath from cachetools import TTLCache -from app.runtime.config import settings +from app.application.configuration import get_token_runtime_config from app.runtime.log import logger from app.runtime.coalesce import ( CoalesceDecision, @@ -43,6 +44,15 @@ _dns_inflight_locks: Dict[str, asyncio.Lock] = {} _dns_inflight_meta_lock = threading.Lock() +def _resource_secret_key() -> str: + """读取 URL 签名密钥快照,并保留旧插件直接导入模块的兼容路径。""" + try: + return get_token_runtime_config().resource_secret_key + except RuntimeError: + legacy_settings = importlib.import_module("app.runtime.config").settings + return legacy_settings.RESOURCE_SECRET_KEY + + class UrlSafetyReason(str, Enum): """ `evaluate_url_safety` 返回的诊断原因枚举。 @@ -447,7 +457,7 @@ class SecurityUtils: 或显式轮换密钥时所有旧签名一起作废。 """ return hmac.new( - settings.RESOURCE_SECRET_KEY.encode("utf-8"), + _resource_secret_key().encode("utf-8"), SecurityUtils._url_signature_payload(url, purpose), sha256, ).hexdigest() diff --git a/docs/refactor/backend-architecture-next-stage.md b/docs/refactor/backend-architecture-next-stage.md index 9dcaebdaf..5cec767a8 100644 --- a/docs/refactor/backend-architecture-next-stage.md +++ b/docs/refactor/backend-architecture-next-stage.md @@ -70,7 +70,7 @@ MoviePilot V3 当前不是“目录混乱、必须推倒重来”的状态。第 | legacy 默认模块契约 | 0 个宿主观察方法;未知动态方法保留 fallback | 所有静态宿主方法已有显式 V2 spec;真实 fallback 命中由 `module.contract.legacy_hit` 观测 | | 事件枚举 | 53 | 66 个静态 producer、15 个静态 consumer | | 专用 EventData model | 53 | Event Contract Registry 已为全部事件登记 typed payload/fallback 原因 | -| 直接读取 `settings` 的文件 | 127 | 仍按模块族迁移,动态协议和安全端口暂保留 | +| 直接读取 `settings` 的文件 | 122 | 仍按模块族迁移,动态协议和安全端口暂保留 | | `SystemConfigOper()` | 1 个 | 仅组合根创建 `SystemConfigService` 时保留 | | Model 上的 DB 查询装饰器 | 119 | `db_update`/`async_db_update` 为 0;查询 ABI 继续按 canonical 用例迁移 | | 路由端点 | 335 | 11 个已装饰端点超过 80 行,最大 400 行 | @@ -1017,6 +1017,7 @@ MFA/Passkey 专项测试与架构门禁通过,密钥类配置仍保留在安 2026-08-23 将工作流动作 `FetchMediasAction` 和 `SendMessageAction` 接入 `ChainRuntimeConfig` 快照,分别移除内部 API 端口/令牌及工作流链接的全局 `settings` 读取;保留动作公开入口与消息载荷行为,新增快照注入测试覆盖。配置债务由 127 个文件降至 125 个文件,宿主依赖与配置基线已更新。 2026-08-23 将 API 路由前缀作为组合根参数传入 `init_routers`,移除路由初始化模块对全局 `settings` 的直接读取;默认参数保留旧调用兼容性,并补充自定义前缀测试。配置债务由 125 个文件降至 124 个文件。 2026-08-23 将令牌编解码的密钥与过期策略接入 `TokenRuntimeConfig` 快照;启动组合根统一装配,未装配时保留 SDK/旧插件的动态回退,公开令牌函数签名不变。配置债务由 124 个文件降至 123 个文件,并补充资源/认证令牌回归测试。 +2026-08-23 将 URL 资源签名改为复用 `TokenRuntimeConfig` 的资源密钥快照;未装配时保留旧模块动态回退,签名公开 API 与密钥轮换语义不变。配置债务由 123 个文件降至 122 个文件,并补充安全 URL、媒体服务器和字幕下载回归测试。 **收口记录(2026-08-22)**:`reidentify_cache`、`nettest`、`scrape`、OpenAI `chat_completions/responses`、`get_logging` 和 Web Agent SSE 均改为稳定公开入口委托私有编排实现;四个消息交互 Handler 的公开方法也保留 ABI 并委托私有状态机。复杂度基线已清零,API/Application/Chain 入口预算、异步阻塞 ratchet 均通过;复杂度及兼容专项合计 252 项测试通过。 随后将 `TransferChain.do_transfer` 的公开入口收口为稳定兼容 Facade,先提取媒体身份规范化阶段,保留显式 diff --git a/tests/fixtures/architecture/configuration-debt-baseline.json b/tests/fixtures/architecture/configuration-debt-baseline.json index 09f897992..7ae37f367 100644 --- a/tests/fixtures/architecture/configuration-debt-baseline.json +++ b/tests/fixtures/architecture/configuration-debt-baseline.json @@ -9,7 +9,7 @@ "root": "app" }, "settings_imports": { - "count": 123, + "count": 122, "files": [ "app/adapters/cache/backends.py", "app/adapters/cache/redis.py", @@ -47,7 +47,6 @@ "app/agent/tools/impl/send_voice_message.py", "app/agent/tools/impl/update_agent_task.py", "app/agent/tools/impl/update_system_settings.py", - "app/application/security/url.py", "app/cli.py", "app/db/base.py", "app/db/engine.py", diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index 76b7c1661..aee1ed3eb 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -13,8 +13,8 @@ "runtime_to_db": [], "workflow_to_db": [] }, - "edge_count": 6415, - "edge_sha256": "d763c4fede395d6ef881e1308b68cfaf484b82ce2b6f0dec808abf4b1d820858", + "edge_count": 6416, + "edge_sha256": "c138400e259f15da3b22796667691ccca229c9c8b9a4c88f3c122922600c38a1", "edges": [ "app -> app.runtime", "app -> app.runtime.compat", @@ -2705,9 +2705,10 @@ "app.application.security.token -> app.schemas.token", "app.application.security.twofactor -> app.runtime", "app.application.security.twofactor -> app.runtime.log", + "app.application.security.url -> app.application", + "app.application.security.url -> app.application.configuration", "app.application.security.url -> app.runtime", "app.application.security.url -> app.runtime.coalesce", - "app.application.security.url -> app.runtime.config", "app.application.security.url -> app.runtime.log", "app.application.servarr -> app.schemas", "app.application.servarr -> app.schemas.types", diff --git a/tests/test_security_utils.py b/tests/test_security_utils.py index 983dfa719..839e9b4a4 100644 --- a/tests/test_security_utils.py +++ b/tests/test_security_utils.py @@ -69,14 +69,14 @@ class SecurityUtilsTest(TestCase): url = "http://192.168.1.50:8096/Items/abc/Images/Primary" with patch( - "app.application.security.url.settings.RESOURCE_SECRET_KEY", + "app.runtime.config.settings.RESOURCE_SECRET_KEY", "old-secret-value-aaaaaaaaaaaaaaaaaaaaaaaa", ): signed_url = SecurityUtils.sign_url(url) self.assertEqual(SecurityUtils.verify_signed_url(signed_url), url) with patch( - "app.application.security.url.settings.RESOURCE_SECRET_KEY", + "app.runtime.config.settings.RESOURCE_SECRET_KEY", "new-secret-value-bbbbbbbbbbbbbbbbbbbbbbbb", ): self.assertIsNone(SecurityUtils.verify_signed_url(signed_url))