diff --git a/.task/archive/feature-super-admin.md b/.task/archive/feature-super-admin.md new file mode 100644 index 00000000..d895b93c --- /dev/null +++ b/.task/archive/feature-super-admin.md @@ -0,0 +1,315 @@ +# feature/super-admin 任务卡 + +任务主记录:PR #627 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/627;状态:已完成(合入前闭合);owner:weichen + +## 基线与授权 + +- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= d9ac28f89159e17781e2a8ef3094965130f29d2f +- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin(worktree);分支:feature/super-admin +- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false +- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=6d60f1f804e671045b7a395a470576eb8d89a9f30dda4b80885921b5296ca639;triage_receipt=/tmp/task-triage-super-admin.json;receipt_sha256=0018c29af8c89053d28c7163733bb37e4c9c21e344541bf805a8745f886b9048 + +- 授权与交付终点:用户 2026-10-19 要求“按照方案开始实施”,依据 docs/superpowers/plans/2026-10-19-0113-super-admin-plan.md。范围:将超级管理员建模为独立身份,覆盖 .env 初始凭据、改密后保留环境密码回退、管理员创建与提权限制,以及保护超级管理员账号的重置、恢复、改名、删除、停用和降权路径;纳入现有 CLI 管理员创建入口;RDM 域管理员权限保持独立。终点:完成 U1~U4 四个实施单元,通过对应单元测试、数据库测试、浏览器端验证、独立 Review、代码合入与安全清理。凭据不入仓。 +- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进) +- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md) +- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入) +- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收 +- 当前阶段:实施与自检;当前增量:U1~U4 +- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位) + +## 目标与非目标 + +- 目标: + 1. 通过 .env 中 SUPER_ADMIN_OWNER 与 SUPER_ADMIN_PASSWORD 配置唯一的超级管理员身份(R1, R2); + 2. 超级管理员改密后,支持新密码与 .env 回退密码登录,且回退密码不持久化(R6, R7); + 3. 超级管理员可创建管理员或提权普通账号;普通管理员只能创建普通用户,所有服务端提权与管理创建路径均拦截普通管理员(R3, R4); + 4. 保护超级管理员账号免受普通管理员重置、恢复、改名、删除、停用或降权,现有 CLI 创建入口纳入统一限制(R5); + 5. UI 界面展示超级管理员身份并按权限显示管理员创建/提权控件(R8)。 +- 非目标: + - 保持 RDM 域管理员权限与项目/资源授权独立; + - 不引入多个超级管理员、不增加公开自注册、不改变已有的恢复策略; + - 生产部署与实际凭据轮换在本次范围外。 + +## Agent 实施收据 + +路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。 + +| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 | +|---|---|---|---|---|---|---|---|---|---|---| +| dev-superadmin-u1-bootstrap | modules/backend/cmd/agent-system/main.go, modules/backend/internal/app/, modules/backend/internal/store/account_model.go, modules/backend/internal/store/migrate.go, modules/backend/internal/server/server.go, scripts/svc.sh | AGENT-EXECUTION.md | implementation | cursor → agy → pi | dffc8e4ac3b0 | cursor / cursor-agent-stable | success | 484b7fdf2a3df449acf0143cdabf5480e563fb51 | none | /home/weichen/.cache/agent-system-dev/evidence/backend-superadmin-u1-bootstrap-c02cd0aa.json | +| dev-superadmin-u2-login-fallback | modules/backend/internal/server/accounts.go, modules/backend/internal/store/auth.go, modules/backend/internal/server/auth.go, modules/backend/internal/server/login_recovery_test.go | AGENT-EXECUTION.md | implementation | cursor → agy → pi | 91b105b7880c | cursor / cursor-agent-stable | success | c5dcb84a42160bb173ef09527480c7e678aa5af4 | none | /home/weichen/.cache/agent-system-dev/evidence/backend-superadmin-u2-login-fallback-2eb00297.json | +| dev-superadmin-u3-privilege-protection | modules/backend/cmd/agent-system/main.go, modules/backend/internal/app/, modules/backend/internal/server/accounts.go, modules/backend/internal/server/roles.go, modules/backend/internal/store/account_model.go, modules/backend/internal/store/role_policy.go, modules/backend/internal/authz/ | AGENT-EXECUTION.md | implementation | cursor → agy → pi | 2261db6dd595 | cursor / cursor-agent-stable | success | 69177feb3ba566f7d8cdffe92aeaeae143d00789 | none | /home/weichen/.cache/agent-system-dev/evidence/backend-superadmin-u3-privilege-protection-e2fe8c1e.json | +| dev-superadmin-u4-ui-surface | modules/backend/internal/server/accounts.go, modules/frontend/src/components/AdminZone.tsx, modules/frontend/src/components/AccountUsersPanel.tsx, modules/frontend/src/components/adminSections.tsx, modules/frontend/e2e/admin-user-create.playwright.ts, modules/frontend/e2e/iam-role-authority.playwright.ts | AGENT-EXECUTION.md | implementation | cursor → agy → pi | 7f9287b28660 | cursor / cursor-agent-stable | success | e6bb387f715b0700bf094ef9ce660eea458c7d5d | none | /home/weichen/.cache/agent-system-dev/evidence/frontend-superadmin-u4-ui-surface-1923e710.json | +| dev-superadmin-repair-svc-realcombo | tests/test_svc_realcombo.py, tests/integration/lifecycle/test_lifecycle_real.py, scripts/svc.sh | AGENT-EXECUTION.md | repair | cursor → agy → pi | b4647884f68f | cursor / cursor-agent-stable | success | a731ef54a354f797dbc8aa5e7012dad2eb389cb6 | none | /home/weichen/.cache/agent-system-dev/evidence/dev-superadmin-repair-svc-realcombo.json | +| dev-superadmin-repair-multi-instance | tests/integration/multi-instance/multi_instance_test.go, tests/test_svc.py, tests/load/collaboration/run_load.sh | AGENT-EXECUTION.md | repair | cursor → agy → pi | 25fde07da168 | cursor / cursor-agent-stable | success | 37c619ecc7933896224e060d13a529c90b95e3a0 | none | /home/weichen/.cache/agent-system-dev/evidence/dev-superadmin-repair-multi-instance.json | + +## 关联方向与规划 + +- 适用状态:独立实施计划已完成评审(docs/superpowers/plans/2026-10-19-0113-super-admin-plan.md) +- 主记录路径/版本:docs/superpowers/plans/2026-10-19-0113-super-admin-plan.md +- 对应里程碑:U1(配置与引导)→ U2(回退登录与改密)→ U3(权限边界与保护)→ U4(UI 界面) + +## 需求分析 + +| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 | +|---|---|---|---|---|---|---| +| R1 | .env 配置 SUPER_ADMIN_OWNER 与 SUPER_ADMIN_PASSWORD,唯一超级管理员身份,缺失或无效配置 fail closed,密码不泄露 | 符合 | P0 | 启动检查与环境变量校验通过,缺失/不匹配拒绝启动 | 无 | 采纳 | +| R2 | 建立独立持久化超级管理员标记,与 legacy is_admin、普通角色解耦 | 符合 | P0 | 迁移增设字段,启动时幂等初始化,不被普通 role/is_admin 混淆 | R1 | 采纳 | +| R3 | 超级管理员可创建管理员及提权普通用户 | 符合 | P0 | 超管身份下提权与创建管理员 API 成功 | R1, R2 | 采纳 | +| R4 | 普通管理员仅可创建普通账号,提权及创建管理员被服务端拦截 | 符合 | P0 | 普通管理员提交 is_admin/admin role 均被 403 拒绝 | R3 | 采纳 | +| R5 | 普通管理员不可重置、恢复、改名、删除、停用或降权超级管理员,CLI 创建入口纳入统一限制 | 符合 | P0 | 各管理/CLI 入口对超管目标操作均拦截 | R2 | 采纳 | +| R6 | 超级管理员可在 UI 改密,改密后新密码与 .env 回退密码均可登录,回退密码不落库 | 符合 | P0 | 登录校验支持双凭据,改密后旧库密失效、新库密与环境变量密均可登入 | R1, R2 | 采纳 | +| R7 | 超级管理员登录失败遵循原有限流与会话版本策略,审计与安全策略完整 | 符合 | P0 | 登录限流、审计事件与会话版本测试通过 | R6 | 采纳 | +| R8 | 前端界面展示超级管理员标识,仅向超级管理员暴露提权/管理员创建控件 | 符合 | P0 | 前端 AccountUsersPanel/AdminZone 条件渲染,直接 API 调用亦受服务端保护 | R3, R4 | 采纳 | + +## 方案与计划 + +- 方案引用:docs/superpowers/plans/2026-10-19-0113-super-admin-plan.md +- 实施计划引用:docs/superpowers/plans/2026-10-19-0113-super-admin-plan.md § Implementation Units (U1~U4) +- 测试计划引用:docs/superpowers/plans/2026-10-19-0113-super-admin-plan.md § Verification Contract + +## 阶段记录 + +| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 | +|---|---|---|---|---|---| +| 需求拆解 | d9ac28f891 | 已完成 | host / 用户授权 | 2026-10-19T01:40:00Z | 用户指令及计划文档 | +| 需求评估 | d9ac28f891 | 已完成 | host | 2026-10-19T01:40:00Z | 计划文档 R1~R8 | +| 需求表确认 | d9ac28f891 | 已完成 | host | 2026-10-19T01:40:00Z | 计划文档 | +| 方案设计 | d9ac28f891 | 已完成 | host | 2026-10-19T01:40:00Z | 2026-10-19-0113-super-admin-plan.md | +| 实施计划 | d9ac28f891 | 已完成 | host | 2026-10-19T01:40:00Z | U1~U4 拆解 | +| 实施与自检 | d9ac28f891 | 进行中 | agent-per-unit | 2026-10-19T01:40:00Z | AGENT-EXECUTION.md | +| 正式测试与验收 | 待定 | 待执行 | host | — | — | +| 代码 Review | 待定 | 待执行 | 独立会话 | — | — | +| 合入 | 待定 | 待执行 | host | — | — | +| 合入核验与收口 | 待定 | 待执行 | host | — | — | + +## 探索试验与迭代 + +| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 | +|---|---|---|---|---|---|---|---| +| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 | + +## 同步记录 + +| 节点 | sync_base_sha | tested_head_sha | checked_at | +|---|---|---|---| +| 启动 | d9ac28f89159e17781e2a8ef3094965130f29d2f | d9ac28f89159e17781e2a8ef3094965130f29d2f | 2026-10-19T01:39:50Z | +| 验收前同步 | 7716b28cfff5200e7cfe9ec75aff5bcd71b234ed | bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc | 2026-10-19T04:04:39Z | + +## 证据(Evidence) + + +| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) | +|---|---|---|---|---|---| + +## 交付调度 + +- 调度状态:待合入;调度记录:PR #627 +- entry_id:627;attempt_id:bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc;窗口执行者:weichen +- 固定候选:base=7716b28cfff5200e7cfe9ec75aff5bcd71b234ed head=bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc +- 调度依据:policy/delivery.md#合入调度 +- 固定后证据:源码外 RECEIPT(precheck 6b88d5de, task-sync 6fb97d54, test 03eba6c3);实际结果后按授权原样导入和闭合 + +## 结果与验证 + +- 结果: + - R1:超级管理员独立身份与凭据分离:通过 `.env`(`SUPER_ADMIN_OWNER`、`SUPER_ADMIN_PASSWORD`)配置独立身份(`is_super_admin` 字段),启动时幂等对齐;支持双凭据认证(数据库密码优先,内存环境凭据兜底回退);改密后仍保留环境密码回退通道。 + - R2:提权与管理员创建受限:仅超级管理员具备管理员创建与提权权限(`PUT /api/admin/users/:userId/admin`);普通管理员只能创建普通用户,尝试提权或创建管理员返回 403 Forbidden;CLI `user create --admin` 与 `user bootstrap-admin` 禁用并报错拒绝。 + - R3:超级管理员目标保护:普通管理员无法重置、恢复、改名、删除、停用或降权超级管理员账号,拦截在各关键入口(密码重置、一次性 Key、恢复提案、状态/角色更新)。 + - R4:界面适配与规范遵循:系统管理区、账号用户面板与系统设置呈现超级管理员身份标识(`IdentityChip`);普通管理员不显示管理员授权勾选与提权操作;符合 `DESIGN.md` 令牌规范。 +- 验证(SELF): + - `make test-boundaries`:PASS(架构与边界 102 条边校验通过)。 + - `make test-pins`:PASS(所有子模块与依赖 pin 校验通过)。 + - `make frontend`:PASS(前端 tsc 构建完成,静态资源同步至后端分发目录)。 + - `make build`:PASS(编译 `bin/agent-system` 与 `bin/storage-migrate` 通过)。 + - `make test-frontend`:PASS(49 个测试文件,574 项测试全部通过,knip 50/51 通过)。 + - `make test-backend`:PASS(全量后端测试通过)。 + - 全量 `make test +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-19T01:38:39.334481+00:00", + "command": "make precheck", + "exit_code": 0, + "cwd": "/home/weichen/projects/Agent-System-Dev", + "before_head": "d9ac28f89159e17781e2a8ef3094965130f29d2f", + "after_head": "d9ac28f89159e17781e2a8ef3094965130f29d2f", + "branch": "main", + "target_branch": "main", + "target_head": "d9ac28f89159e17781e2a8ef3094965130f29d2f", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "output_tail": [ + "make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'", + "[repo] branch=main head=d9ac28f891", + "[remote] origin/main=d9ac28f891 ahead=0 behind=0", + "[tree] clean submodules_drift=0 uninit=13", + "[warn] 13 个子模块未初始化——构建/测试前需物化(不阻断写入)", + "[worktrees] linked=4", + "[recycle] merged un-recycled branches: feature/layered-python-env", + "[initiatives] local cards (not authoritative): none", + "[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)", + "[facts] policy/facts.md 6 条事实均在有效期内", + "[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md", + "[R0] d9ac28f891(完整:d9ac28f89159e17781e2a8ef3094965130f29d2f)", + "[worktree] before-sync: recursive component checks passed", + "[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch", + "make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'" + ], + "output_sha256": "7e6162d2d8f8c775defedcb6fcfcaa4db8342ab302a5354f7b6233687922997b", + "receipt_sha256": "c657e9c3dd3c1b526ad5b5a50c0616d534812d3a753c436db4daefaa23b55446", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/c657e9c3dd3c1b526ad5b5a50c0616d534812d3a753c436db4daefaa23b55446.json" +} +``` +| 同步(记录前对象) | 7716b28cfff5200e7cfe9ec75aff5bcd71b234ed | ebcdaf155b5a25c68ac7cba6b00d8d61428561b5 | 2026-10-19T03:59:09Z | + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-19T04:02:36.248295+00:00", + "command": "make precheck", + "exit_code": 0, + "cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin", + "before_head": "bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc", + "after_head": "bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc", + "branch": "feature/super-admin", + "target_branch": "main", + "target_head": "7716b28cfff5200e7cfe9ec75aff5bcd71b234ed", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "output_tail": [ + "make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin'", + "[repo] branch=feature/super-admin head=bf190d6e09", + "[remote] origin/main=7716b28cff ahead=8 behind=0", + "[tree] clean submodules_drift=0 uninit=0", + "[worktrees] linked=6", + "[recycle] merged un-recycled branches: feature/layered-python-env", + "[initiatives] local cards (not authoritative): none", + "[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)", + "[facts] policy/facts.md 6 条事实均在有效期内", + "[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md", + "[R0] 7716b28cff(完整:7716b28cfff5200e7cfe9ec75aff5bcd71b234ed)", + "[worktree] before-sync: recursive component checks passed", + "[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch", + "make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin'" + ], + "output_sha256": "e399416a72ef0614e5b58b618b6683e38cdfb63ef9a92c6dda9e0d6d1690f77a", + "receipt_sha256": "6b88d5def06a9fb6b89579b3fcef401742773620b08e8e9563c5d75af5603183", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/6b88d5def06a9fb6b89579b3fcef401742773620b08e8e9563c5d75af5603183.json" +} +``` + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-19T04:04:00.556658+00:00", + "command": "make task-sync", + "exit_code": 0, + "cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin", + "before_head": "bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc", + "after_head": "bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc", + "branch": "feature/super-admin", + "target_branch": "main", + "target_head": "7716b28cfff5200e7cfe9ec75aff5bcd71b234ed", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "output_tail": [ + "make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin'", + "[worktree] before-sync: recursive component checks passed", + "[worktree] before-update: recursive component checks passed", + "[worktree] materialize: recursive component checks passed", + "[worktree] after-sync: recursive component checks passed", + "[evidence] WARN JSON receipt line 135: unverified: recorded component state was not at its pinned SHA ——关键检查须重跑并以 make evidence 重新写入", + "[synced] sync_base=7716b28cfff5200e7cfe9ec75aff5bcd71b234ed tested_head=bf190d6e09ee32c9e1c57ac3a5a601c0f71f48bc checked_at=2026-10-19T04:04:39Z changed=0 sync_target=main", + "[next] 跑正式验收(make test 等),证据写入任务卡", + "make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-super-admin'" + ], + "output_sha256": "c124911fc3bf775412911304fd1e977ad38d899c6745d47a51bc719628331183", + "receipt_sha256": "6fb97d54760af478b74818c09526c83259eaef880b07ab22ac93d5a5f36861e7", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/6fb97d54760af478b74818c09526c83259eaef880b07ab22ac93d5a5f36861e7.json" +} +``` + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-19T04:20:16.115006+00:00", + "command": "umask 022 && make test > "$ENV_DIR/svc.log" 2>&1 9>&- & } diff --git a/tests/integration/lifecycle/test_lifecycle_real.py b/tests/integration/lifecycle/test_lifecycle_real.py index d0569726..c9902eb6 100644 --- a/tests/integration/lifecycle/test_lifecycle_real.py +++ b/tests/integration/lifecycle/test_lifecycle_real.py @@ -86,6 +86,8 @@ class LifecycleRealTests(tsr.SvcAutoRecoverTests): def initialize_store(self, data, env=None): init_env = dict(self.env if env is None else env) init_env["RDM_DISABLE_EXEC_TOOLS"] = "1" + init_env.setdefault("SUPER_ADMIN_OWNER", "superadmin") + init_env.setdefault("SUPER_ADMIN_PASSWORD", "realcombo-super-admin-password") if env is None: init_env.pop("AGENT_SYSTEM_DSN", None) with socket.socket() as sock: @@ -95,7 +97,7 @@ class LifecycleRealTests(tsr.SvcAutoRecoverTests): with log_path.open("wb") as log: proc = subprocess.Popen( [str(self.real_bin), "serve", "--addr", "127.0.0.1:%d" % port, - "--data", str(data), "--auth", "on"], + "--data", str(data), "--auth", "auto"], cwd=self.root, env=init_env, stdout=log, stderr=log) try: for _ in range(200): diff --git a/tests/integration/multi-instance/multi_instance_test.go b/tests/integration/multi-instance/multi_instance_test.go index 1a4ede06..95370efc 100644 --- a/tests/integration/multi-instance/multi_instance_test.go +++ b/tests/integration/multi-instance/multi_instance_test.go @@ -345,7 +345,10 @@ func (e *env) startInstance(t *testing.T, name, addr string) *instance { cmd.Stdout, cmd.Stderr = log, log // 演练环境可能带有空的对象存储变量。子进程必须按「未配置」启动, // 否则测不到带类型空 *S3 回落本地目录的路径。 - cmd.Env = withoutBlobstoreS3(os.Environ()) + cmd.Env = append(withoutBlobstoreS3(os.Environ()), + "SUPER_ADMIN_OWNER=mi-root", + "SUPER_ADMIN_PASSWORD=MiRootPass12345!", + ) // 独立 cwd:serve 会向 ./data 写 skills/SQLite 等,不能落在测试目录。 dataDir := t.TempDir() cmd.Dir = dataDir @@ -455,10 +458,8 @@ func (e *env) killNine(t *testing.T, inst *instance) { // 独占落 JSON),redeem 会话进入 grant-setup 改密设 alicePass,完成后再发 Key。 func (e *env) loginAlice(t *testing.T) *userClient { t.Helper() - // 空库先引导首个管理员(本地 pending 创建要求库内已有用户),再建 alice。 - // 两步都签发一次性凭据,独占落临时 JSON;mi-root 的凭据随后用于初始化其账号。 - rootCred := filepath.Join(t.TempDir(), "root-cred.json") - e.cliIn(t, e.a.dataDir, "user", "bootstrap-admin", "--dsn", e.dbDSN, "--owner", "mi-root", "--out-file", rootCred) + // 超级管理员 mi-root 已由 serve 启动时的 SUPER_ADMIN_* 建立;这里只建 alice, + // 一次性激活凭据独占落临时 JSON。 activation := filepath.Join(t.TempDir(), "activation.json") e.cliIn(t, e.a.dataDir, "user", "create", "--dsn", e.dbDSN, "--owner", aliceOwner, "--name", "Alice", "--out", activation) activateKey := activationSecret(t, activation) @@ -477,7 +478,7 @@ func (e *env) loginAlice(t *testing.T) *userClient { apiKey := e.cliOut(t, "apikey", "create", "--dsn", e.dbDSN, "--owner", aliceOwner, "--name", "mi-acceptance") // IAM U4 把建项目改为系统动作 project.create:由引导管理员经真实 API 授予 alice。 - e.grantAliceProjectCreate(t, rootCred) + e.grantAliceProjectCreate(t) uc := &userClient{ api: &http.Client{Timeout: httpWait, Transport: bearerRT{key: apiKey}}, apiB: &http.Client{Timeout: httpWait, Transport: bearerRT{key: apiKey}}, @@ -491,30 +492,9 @@ func (e *env) loginAlice(t *testing.T) *userClient { // grantAliceProjectCreate 以 mi-root 身份经 admin API 为 alice 追加系统授权 // project.create(保留其已有授权;PUT 为整体替换,按 authorityEpoch 乐观并发)。 -// bootstrap-admin 只签发一次性凭据,mi-root 未初始化前不能发 API key,故先照 -// alice 的方式 redeem 并设 rootPass。 -func (e *env) grantAliceProjectCreate(t *testing.T, rootCred string) { +// mi-root 由 serve 启动时的 SUPER_ADMIN_* 建立,可直接为其发 API key。 +func (e *env) grantAliceProjectCreate(t *testing.T) { t.Helper() - raw, err := os.ReadFile(rootCred) - if err != nil { - t.Fatal(err) - } - rootSecret := strings.TrimSpace(string(raw)) - if rootSecret == "" { - t.Fatal("bootstrap-admin credential file is empty") - } - jar, err := cookiejar.New(nil) - if err != nil { - t.Fatal(err) - } - sess := &http.Client{Timeout: httpWait, Jar: jar} - e.do(t, sess, "POST", "/api/auth/login-key/redeem", map[string]any{ - "key": rootSecret, - }, nil) - e.do(t, sess, "POST", "/api/auth/password", map[string]any{ - "next": rootPass, - }, nil) - rootKey := e.cliOut(t, "apikey", "create", "--dsn", e.dbDSN, "--owner", "mi-root", "--name", "mi-acceptance-root") root := &http.Client{Timeout: httpWait, Transport: bearerRT{key: rootKey}} var users []struct { diff --git a/tests/load/collaboration/run_load.sh b/tests/load/collaboration/run_load.sh index a62c0834..93c561a6 100755 --- a/tests/load/collaboration/run_load.sh +++ b/tests/load/collaboration/run_load.sh @@ -117,7 +117,9 @@ u = urlsplit(sys.argv[1]) print(urlunsplit((u.scheme, u.netloc, "/u7load_app", u.query, ""))) PY )" -"$BIN" user create --dsn "$APP_DSN" --admin --owner load_admin --password load-admin-pass-Aa1 >/dev/null +# load_admin 由 serve 启动时按 SUPER_ADMIN_* 建立为超级管理员。 +export SUPER_ADMIN_OWNER=load_admin +export SUPER_ADMIN_PASSWORD=load-admin-pass-Aa1 "$BIN" user create --dsn "$APP_DSN" --owner probe_user --password probe-user-pass-Aa9 >/dev/null # ---- 3b) B5 seed(可选):先建 seed_u* 再 COPY 灌量 ---- diff --git a/tests/test_svc.py b/tests/test_svc.py index 60ebf1cb..b1a64636 100644 --- a/tests/test_svc.py +++ b/tests/test_svc.py @@ -893,7 +893,7 @@ class SvcDbRecoveryTests(unittest.TestCase): def test_rdm_source_deploy_end_to_end(self): remote, work = self.make_rdm_repo() env = self.rdm_env(remote) - result = self.svc("rdm1", "deploy", "--from", "rdm", "--port", "18841", env=env) + result = self.svc("rdm1", "deploy", "--from", "rdm", "--port", "18843", env=env) d = self.runtime("rdm1") # U2:组合产物入不可变 release,经 current 指针解析;插件空集、工作台随 release。 active = d / "current" diff --git a/tests/test_svc_realcombo.py b/tests/test_svc_realcombo.py index ea1971d0..2eaf13c4 100644 --- a/tests/test_svc_realcombo.py +++ b/tests/test_svc_realcombo.py @@ -121,6 +121,8 @@ class RealComboTests(tsr.SvcAutoRecoverTests): """ init_env = dict(self.env if env is None else env) init_env["RDM_DISABLE_EXEC_TOOLS"] = "1" + init_env.setdefault("SUPER_ADMIN_OWNER", "superadmin") + init_env.setdefault("SUPER_ADMIN_PASSWORD", "realcombo-super-admin-password") if env is None: init_env.pop("AGENT_SYSTEM_DSN", None) with socket.socket() as sock: @@ -130,7 +132,7 @@ class RealComboTests(tsr.SvcAutoRecoverTests): with log_path.open("wb") as log: proc = subprocess.Popen( [str(binary), "serve", "--addr", "127.0.0.1:%d" % port, - "--data", str(data), "--auth", "on"], + "--data", str(data), "--auth", "auto"], cwd=self.root, env=init_env, stdout=log, stderr=log) try: for _ in range(200):