diff --git a/project-docs/V2-product-audit-and-development-backlog-2026-08-01.md b/project-docs/V2-product-audit-and-development-backlog-2026-08-01.md index ca5096f8..5c061f88 100644 --- a/project-docs/V2-product-audit-and-development-backlog-2026-08-01.md +++ b/project-docs/V2-product-audit-and-development-backlog-2026-08-01.md @@ -449,3 +449,22 @@ legal_name: string | null 4. **P1 平台管理员完整治理与并发验收**:继续补充受控授予/撤销、支持访问原因和时限、双人审批及专用审计。最后有效管理员保护虽已通过本地定向回归,但仍需在真实 PostgreSQL 上执行双请求并发集成验收,不能以 SQLite 或单请求结果替代。 5. **原 9.18 第 5 项已完成并移出阻断**:开发环境工作空间口径、组织创建命令和管理员密码输入语义已修正;已登录访问 `/login` 自动返回工作台,未登录及其他公开身份入口保持原行为。Web 路由 `4/4`、工作空间状态 `3/3`、身份页源码 `1/1`、会话映射 `2/2`、TypeScript 和登录态浏览器刷新复核均通过;后续如改变登录或切换账号策略,应作为独立会话需求重新评审,不回退本次已验收语义。 6. **P2 运营与测试效率**:补齐个人套餐、配额、用量、支付、数据导出/删除和客户成功指标;在不降低密码哈希参数的前提下并行化身份测试夹具,并建立账号冲突诊断的规模化查询和回归性能基线。 + +### 9.21 第七阶段自动化开发结果(2026-08-02) + +- **账号冲突诊断扩展性收口**:SQL 仓储不再把全局账号、租户用户、成员关系、工作空间和两类凭据整表加载为 ORM 对象,而是先以 SQL 发现异常候选,再沿账号、用户、成员关系和规范化邮箱组成的关系图加载候选详情。健康规模数据保持零误报,异常详情继续复用原冲突码、严重度、稳定诊断 ID 和最小披露模型,不增加身份写操作或迁移。 +- **规范化与高基数边界**:数据库诊断表达式使用与 Python `str.strip()` 等价的完整 Unicode 首尾空白集合;SQLite 对非 ASCII 大小写差异增加兼容候选扩展,PostgreSQL 使用 `lower(btrim(...))`。账号、用户、成员、凭据、邮箱和工作空间关联加载统一按每批 400 个值执行,避免大量异常候选超过 SQLite/PostgreSQL 绑定参数上限。缺失全局账号发现由相关子查询改为规范化账号邮箱集合的反连接,避免 SQLite 对每个用户重复扫描账号表。 +- **SMTP 协议与配置安全**:验证码和邀请统一走一条 SMTP 发送路径;STARTTLS 与 SMTPS 互斥,生产禁止明文 SMTP,用户名与密码必须成对出现,环境密码与密码文件必须二选一。密码文件必须为可读、非空 UTF-8,SMTP 密码不进入对象 `repr`;发件人、收件人和显示名执行地址及头注入校验,邮件增加 `Date`、`Message-ID`、自动回复抑制头,底层 SMTP、网络和 Unicode 编码错误统一脱敏为 `SMTP delivery failed.`。 +- **生产 SMTP secret 闭环**:默认 `disabled` 模式继续使用管理员线下交付邀请链接,不要求 SMTP secret。新增可选 `docker-compose.production.smtp.yml`,仅在启用 SMTP 时把宿主机受保护密码文件只读挂载到固定容器路径,并清空直接密码环境变量。生产 preflight 同步校验 TLS 模式、主机、端口、超时、发件邮箱、头注入、HTTPS 非 loopback 公网地址、宿主机 secret 的绝对路径、非空和 POSIX owner-only 权限。 +- **组织工作空间代开权限闭环**:产品语义确定为“管理后台仅由平台管理员代开组织工作空间”,不改变公开自助注册这一独立旅程。签名 Web 会话兼容携带 `platform_permissions`;身份页只有会话能力与平台管理员名册实时验证同时成功时才显示代开表单,提交函数再次防护,Next.js 身份代理对 `register-company` 再执行实时平台权限复核。企业和个人工作空间管理员仍可管理本空间成员,但不能从管理后台代开新组织后落入旧会话导致 `403`。 + +### 9.22 第七阶段验证证据与后续优先项 + +- Contract API 使用全新隔离 SQLite 执行全量 `211/211` 通过;最终账号冲突、SMTP 和身份安全定向回归 `32/32`,生产发布工具 `24/24`,Web 定向回归 `14/14`,TypeScript 类型检查和实现 Agent 的 100 路由生产构建均通过。Python 编译、三份 Compose YAML 解析、生产示例 preflight 和 `git diff --check` 通过;差异检查仅有工作区既有 CRLF 提示。 +- 账号冲突回归覆盖 300 组健康身份零误报、孤立成员、孤立全局/旧凭据、多旧凭据无统一凭据、Unicode/NBSP/Tab 规范化、关系图闭包稳定性,以及 1,001 个同时冲突候选。高基数用例确认单条 SELECT 绑定参数保持在分片上限内;响应递归检查继续不含密码哈希、token、验证码或会话秘密。 +- SMTP 验收使用本机 TCP 假 SMTP 服务与真实 Python `smtplib` 完成协议命令和邮件内容往返;这证明应用 SMTP 客户端路径可运行,不代表外部邮件服务已接受、送达、退信或重试成功。当前机器没有 Docker CLI,未执行真实 `docker compose config` 或容器 secret 挂载;没有生产 SMTP 主机和凭据,未进行外部邮件投递。 +- **P0 法律签批与共享预发布交付**仍未完成:需具名法务复核知识卡和黄金结果,建立权威法条、正式版本、生效记录、批准责任人,并在共享预发布环境持久化验收运行 ID。技术门禁、开发浏览器和本机协议测试均不能替代法律批准。 +- **P1 外部邮件与账号恢复运营**:接入实际选定的 SMTP/邮件服务后,执行发送、送达、退信、重试、限流和浏览器端注册/验证/邀请/重置全旅程;继续设计全局登录邮箱变更的旧邮箱确认、新邮箱验证、冲突检查和恢复流程。 +- **P1 PostgreSQL 性能与并发验收**:在共享预发布 PostgreSQL 上记录账号候选 SQL 的 `EXPLAIN (ANALYZE, BUFFERS)`、排序规则和高冲突基数基线,按证据决定是否新增规范化邮箱列或表达式索引;同时执行最后有效平台管理员保护的真实双请求并发测试。当前 SQLite、SQL 编译和分片测试不能替代该验收。 +- **P1 高权限写操作治理**:账号合并、拆分、成员恢复以及平台管理员授予/撤销仍必须先定义 preview、幂等 operation ID、支持访问原因与时限、双人审批、回滚策略和 before/after 审计,不得从当前只读诊断直接扩展为无审批破坏性写接口。 +- **P2 产品表达与自动化**:工作空间类型在单空间和多空间状态下仍需以独立“系统/个人/组织”标签一致表达;平台专属诊断页应增加前端精确路由守卫和真实 Route Handler 测试,覆盖当前权限、已撤权、旧会话和后端不可用四种状态。 diff --git a/yuqei-contract-platform/apps/contract-web/src/app/admin/identity/page-content.test.mjs b/yuqei-contract-platform/apps/contract-web/src/app/admin/identity/page-content.test.mjs index 20cf179a..28578d7d 100644 --- a/yuqei-contract-platform/apps/contract-web/src/app/admin/identity/page-content.test.mjs +++ b/yuqei-contract-platform/apps/contract-web/src/app/admin/identity/page-content.test.mjs @@ -3,6 +3,10 @@ import { readFile } from "node:fs/promises"; import test from "node:test"; const pageSource = await readFile(new URL("./page.tsx", import.meta.url), "utf8"); +const identityProxySource = await readFile( + new URL("../../api/contract-platform/identity/[...path]/route.ts", import.meta.url), + "utf8", +); test("organization workspace creation does not expose a default administrator password", () => { assert.doesNotMatch(pageSource, /admin654321/i); @@ -13,3 +17,18 @@ test("organization workspace creation does not expose a default administrator pa assert.doesNotMatch(pageSource, /placeholder="公司名称"/); assert.doesNotMatch(pageSource, />\s*创建公司\s*); }); + +test("organization workspace provisioning is gated by platform identity management", () => { + assert.match(pageSource, /canManagePlatformIdentity\(/); + assert.match(pageSource, /platformAdministrators !== null/); + assert.match(pageSource, /if \(!canCreateOrganizationWorkspace\)/); + assert.match(pageSource, /\{canCreateOrganizationWorkspace &&