ymq 702f27407b refactor(auth): 移除全部自造鉴权/会话/语义中间件,回归标准 ahserver+rbac
根因:元景迭代中为通过 QC 门禁反复叠加了 24 个自定义中间件
(auth_gate_strict / session_gate / login_param_source / route_guard /
 entry_semantics / http_semantics / notfound / get_only_405 / ...),
导致标准登录链路被破坏:
  - auth_gate_strict 手写 Fernet 解密 + 用 aiohttp_session 的会话创建时间
    created 按 1 天判过期 → 所有受保护端点带 cookie 仍 401
  - route_guard 的 _has_business_params 用 await request.read() 消费 POST body
    → up_login 收到 params_kw={} → password_encode(None) 崩 500

改动:
  - app/scense.py 回归 57 行标准入口:ServerEnv + load_appbase/load_rbac/
    load_pybricks + 业务模块 load_*,零自定义中间件
  - 24 个自造中间件归档到 app/_archive_disabled_20261028_151505/(可回滚)
  - /healthz 改用 ahserver 标准 FunctionProcessor(RegisterFunction 注册 +
    conf.website.startswiths 映射),不再用中间件拦截

实测闭环(127.0.0.1:9380 + https://gbak.opencomputing.cn):
  healthz 200 | 匿名 401 | admin 登录 200+cookie | 带cookie 200 |
  伪造cookie 401 | 注册 200+自动登录 | 新用户登录 200+cookie |
  新用户访问 403(已认证无权限,RBAC 正确行为)| 登出 200 |
  错误密码 200 业务信封(不崩 500)

register/login/logout/session 全部由 rbac 模块 up_login.dspy /
register.dspy / logout.dspy + ahserver session 中间件提供。
2026-10-28 15:19:43 +08:00

134 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""宿主侧命名空间注入接线(T4 dspy 命名空间符号注入族)。
【为什么在宿主侧还有一层】ahserver 的 .dspy exec 命名空间由框架在每次请求构造,
模块 init() 的 `env.xxx = xxx` 只有在**宿主已调用 load_{模块}()** 时才生效;基础模块
(rbac / appbase / bricks)是 referenced_modules(只读,禁改源码),其端点若依赖
`env.<handler>` 而该符号未挂/为 None,表现就是 `TypeError: 'NoneType' object is not callable`
(R42 集群⑤)。宿主侧做两件确定性的事:
1) `install_module_shim(env)` —— 把业务模块的统一 dispatch 入口挂到 ServerEnv
(scense.ns_shim.install;scense_demo 复用同一入口,不重复实现)。
2) `backfill_base_handlers(env)` —— 对只读基础模块,用包内真实存在且可调用的函数
补齐 `env.<name>`(**只 setattr 运行时命名空间,不改基础模块源码**);
包内也不存在的符号不伪造,交由端点侧 ns-shim 给 `symbol-missing:<op>` 结构化 503。
接线点:应用入口 init() 在全部 load_*() 之后调用本模块 `wire_ns_shim(env)`,并把
`env.NS_SHIM_WIRING` 落一条自证字符串,供部署态核验。
"""
import importlib
NS_WIRING_VERSION = "t4-host-wiring-1"
# 只读基础模块的 handler 候选(包内存在才补;不存在不伪造)
BASE_HANDLER_CANDIDATES = (
("rbac", ("find_unauth_files", "list_path_roles", "list_roles", "list_permissions")),
("appbase", ("get_code", "get_appcodes_kv", "get_appcodes", "get_svgicon")),
("bricks", ("list_tables", "tableinfo", "channels")),
)
def _get(env, name):
try:
return getattr(env, name, None)
except Exception:
return None
def _set(env, name, value):
try:
setattr(env, name, value)
return True
except Exception:
pass
try:
env[name] = value
return True
except Exception:
return False
def install_module_shim(env):
"""业务模块统一 dispatch 入口(scense 域)。返回已挂载入口名清单。"""
installed = []
if _get(env, "dspy_ns_call") is not None:
installed.append("dspy_ns_call(pre-existing)")
try:
from scense.ns_shim import install as _install_scense
_install_scense(env)
installed.append("scense.ns_shim.install")
except Exception as exc: # 模块未装/未加载都不是致命错误,但要留痕
installed.append("scense.ns_shim.install:FAIL:%s" % exc)
return installed
def backfill_base_handlers(env):
"""只读基础模块 handler 补齐。返回 {module: [补齐符号]}。"""
report = {}
for modpath, names in BASE_HANDLER_CANDIDATES:
try:
mod = importlib.import_module(modpath)
except Exception:
report[modpath] = []
continue
fixed = []
for name in names:
fn = getattr(mod, name, None)
if fn is None or not callable(fn):
continue
if _get(env, name) is None:
if _set(env, name, fn):
fixed.append(name)
report[modpath] = fixed
return report
def matrix(env):
"""注入生效矩阵(部署态可经 /scense/api 侧或脚本读取)。"""
rows = []
try:
from scense.ns_shim import ns_shim_matrix
rows = ns_shim_matrix(env).get("rows", [])
except Exception:
rows = []
base = {}
for modpath, names in BASE_HANDLER_CANDIDATES:
base[modpath] = {
name: {
"env_has": _get(env, name) is not None,
"module_has": _module_has(modpath, name),
} for name in names
}
return {"version": NS_WIRING_VERSION, "ops": rows, "base_handlers": base}
def _module_has(modpath, name):
try:
mod = importlib.import_module(modpath)
except Exception:
return False
return callable(getattr(mod, name, None))
def wire_ns_shim(env):
"""入口接线:install + backfill + 自证。永不抛异常(启动期不能让接线阻断服务)。"""
out = {"version": NS_WIRING_VERSION, "installed": [], "backfilled": {}, "error": None}
try:
out["installed"] = install_module_shim(env)
except Exception as exc:
out["error"] = "install:" + str(exc)
try:
out["backfilled"] = backfill_base_handlers(env)
except Exception as exc:
out["error"] = (out["error"] or "") + "|backfill:" + str(exc)
summary = "; ".join(
["installed=" + ",".join(out["installed"])] +
["%s=%s" % (k, ",".join(v) or "-") for k, v in out["backfilled"].items()]
)
try:
env.NS_SHIM_WIRING = NS_WIRING_VERSION + " " + summary
env.ns_shim_matrix = lambda: matrix(env) # noqa: E731 部署态自检入口
except Exception:
pass
return out