Skip to content

feat: 完善 OIDC 页面级分析连接与会话隔离 - #239

Open
cipherTing wants to merge 2 commits into
Gracker:mainfrom
cipherTing:cipherTing-main
Open

feat: 完善 OIDC 页面级分析连接与会话隔离#239
cipherTing wants to merge 2 commits into
Gracker:mainfrom
cipherTing:cipherTing-main

Conversation

@cipherTing

Copy link
Copy Markdown
Contributor

变更说明

  • 将 OIDC 分析连接收敛为当前页面、当前 Trace 的 scoped Lease;Perfetto Viewer 始终走本地 WASM,不再复用全局 HTTP RPC target。
  • 登录、刷新、Trace 切换、退出和面板卸载时清理 AI 会话、SSE、overlay、Lease 与临时运行态,避免旧页面继续写回。
  • OIDC 存储投影移除 trace/session/run/lease/target 等绑定信息;非 OIDC 的 direct-port 与 stale Lease recovery 保持不变。
  • 修复 OIDC cookie mutation 的 CSRF transport,并补齐 runtime bootstrap、trace metadata 路由和回归覆盖。
  • 同步提交与子仓 commit 一致的 frontend/ 预构建产物,便携/Docker 路径无需重新编译 UI。

依赖

验证

  • node ui/build.mjs --no-depscheck 通过;npm run check:frontend-prebuild 通过。
  • OIDC 定向 Vitest 与 frontend server health 回归通过。
  • 真实 OIDC 登录后,已验证 Trace/WASM Viewer、页面范围 AI connection ready,且分析请求能进入 Agent 执行阶段;此前 /agent/analyze 的 CSRF 403 已消失。
  • 已运行 npm run verify:pr:governance、文档、i18n、rendering、quality、frontend prebuild、Rust fmt 均通过;本机的 Debian cargo 1.75 无法解析仓库的 Cargo.lock v4,因此在 cargo check 前停止。该限制来自本地 Rust toolchain 版本,非本次源码断言失败。

Gracker commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Review 结论:FIX_FIRST(当前不要合入)

本轮评审针对根仓 PR 头提交 00d4aab424854115edb234ab57a827164f365efb,以及它引用的 Perfetto 子仓提交 3a2bb0cd7cab1461e69d5266d2e28fc5a8dd73c0Gracker/perfetto#15)。

整体架构方向是合理的:Viewer 继续使用页面本地 WASM,AI 后端连接使用当前页面/当前 Trace 的 scoped lease;OIDC 身份、SSE、overlay、session 与持久化数据也在向页面级隔离收敛。但是当前存在两个必须先修的功能问题,以及一个必须遵守的子模块落地顺序。请按下面 1~10 项逐项完成,不要只让 CI 变绿而跳过行为边界。

1. 修复非 OIDC 请求被无条件改成 credentials: 'include'

位置(在 Gracker/perfetto#15 中):

  • ui/src/core/smartperfetto_backend_fetch.ts:19-22
  • 受影响调用面包括 provider、codebase、critical-path、AI panel 等通过 fetchSmartPerfettoBackend() 访问后端的路径。

当前实现先写入:

credentials: init.credentials ?? 'include'

然后再调用 withSmartPerfettoAuth()。但 withSmartPerfettoAuth() 在非 OIDC 模式会原样返回 init,因此 local/API-key 模式也被强制设置成了 include。这扩大了 cookie/CORS 行为边界,并且与已有非 OIDC 请求契约冲突。

期望行为必须保持为:

场景 期望
OIDC,调用方未指定 credentials credentials: 'include'
OIDC mutation credentials: 'include',并按现有逻辑附加 CSRF header
非 OIDC,调用方未指定 credentials 不新增 credentials 字段,保持浏览器默认行为
非 OIDC,调用方显式指定 credentials 保留调用方的显式值
OIDC 401 继续触发当前的 authority invalidation/reload 路径
非 OIDC 401 不清除 OIDC page authority 状态

推荐的最小修复是让共享 helper 直接复用已有条件化策略:

return fetch(input, withSmartPerfettoAuth(init))
  .then(handleSmartPerfettoAuthResponse);

如果采用其他实现,也必须满足上面的行为表。不要通过把所有旧测试的期望改成 include 来“修复”红灯;这里需要修的是 OIDC 边界外的行为扩张。

2. 修正并补全 credentials 回归测试

当前 PR 新增的 ui/src/core/smartperfetto_backend_fetch_unittest.ts 第一条测试名称说验证 HttpOnly OIDC cookie,但测试本身没有启用 OIDC,导致它把错误的全局默认行为固化了。

请至少完成这些测试:

  1. 测试 OIDC 默认 cookie 时,显式设置:
    window.__SMARTPERFETTO_CONFIG__ = {oidcEnabled: true};
    并准备需要的有效 session。
  2. 新增非 OIDC 测试:未显式传 credentials 时,最终 fetch init 的 credentials 必须是 undefined
  3. 保留 OIDC mutation 的 CSRF + include 覆盖。
  4. 保留非 OIDC 401 不触发 OIDC authority invalidation 的覆盖。
  5. 保留显式 credential mode 的覆盖,确保调用方明确传值时不会被非 OIDC helper 改写。
  6. 以下已有测试必须在不篡改其非 OIDC契约的前提下重新通过:
    • ui/src/plugins/com.smartperfetto.AIAssistant/codebase_api_unittest.ts:51
    • ui/src/plugins/com.smartperfetto.AIAssistant/codebase_api_unittest.ts:107
    • ui/src/plugins/com.smartperfetto.AIAssistant/codebase_api_unittest.ts:203
    • ui/src/plugins/com.smartperfetto.AIAssistant/provider_events_unittest.ts:106

已复现证据:

  • feat: 完善 SmartPerfetto OIDC 会话与分析连接隔离 perfetto#15 全量 UI Vitest:202 个 test files 中 200 passed、2 failed;3150 tests passed、1 skipped、4 failed。
  • 4 个失败全部是 expected credentials to be undefined, received "include"
  • 同一条全量测试命令在基线子仓提交 4a4fae46a08cac4177cdb6339832729121272c19 上:195 个 test files 全部通过,3095 tests passed、1 skipped。
  • 因此这不是本机既有环境噪声,而是本 PR 引入的契约冲突。

3. 修复 OIDC session 失效后 AI Panel 的无保护身份读取

相关控制流:

  • ui/src/core/smartperfetto_request_context.ts
    • OIDC 模式下,tenant/user/workspace getter 会调用 requireSmartPerfettoAuthSession()
    • session 不存在时会抛异常;这是正确的 fail-closed 语义,不应改成默认身份。
  • ui/src/plugins/com.smartperfetto.AIAssistant/ai_panel.ts:3517
    • view() 无保护调用 getSmartPerfettoRequestContext()
  • ui/src/plugins/com.smartperfetto.AIAssistant/ai_panel.ts:6768 附近
    • analysis-context 保存/加载路径也会无保护读取 request context。
  • handleOidcAuthSessionChange()
    • session 过期、logout、401 或跨 tab invalidation 后会清理运行态并触发 redraw。

初始未登录页面虽然由 index.html bootstrap gate 拦住,但 session 可以在 AIPanel 已挂载后失效。此时 redraw 仍会进入上述无保护调用,导致渲染或后续设置路径抛异常,而不是稳定展示重新登录/不可用状态。

修复要求:

  1. OIDC 模式下,如果 getSmartPerfettoAuthSession() 不存在,AIPanel 必须进入明确的 unauthenticated/auth-expired UI 状态。
  2. 在该状态下,不得调用任何需要 tenant/user/workspace 的读取、保存或恢复函数。
  3. 不得回退到 localStorage 中的默认 tenant/user/workspace,也不得伪造占位 identity;OIDC 必须继续 fail closed。
  4. session invalidation 后继续完成现有 teardown:停止 SSE/异步请求、清理 overlay/note、清空 agent session/run/trace/lease 绑定、使旧 generation 的异步回调失效。
  5. auth-expired 状态应禁用发送分析、保存 analysis context、恢复旧会话等依赖身份的动作,并提供明确的重新登录或刷新提示。
  6. session 恢复后,只能从新的服务端 session 重新构造 request context,并重新加载这个新 identity scope 下的状态;旧用户/旧 workspace 的状态不得复活。
  7. 入口保护需要覆盖 oninitview、settings/analysis-context change、auth invalidation 后 redraw,以及 panel 重新挂载/模式切换,不要只在其中一个调用点加 try/catch。
  8. 建议提供一个语义清晰的“可选 OIDC request context”入口或 AIPanel 级 guard;不要在多个调用点散落吞异常的 catch。

4. 为 OIDC no-session / auth-expiry 补回归测试

至少新增以下场景:

  1. oidcEnabled: true 且 session 为 undefined 时,AIPanel 初始化和 view() 不抛异常。
  2. 有效 OIDC session 下挂载 AIPanel,然后清除 session 并触发 smartperfetto-auth-session-changed,下一次 redraw 不抛异常。
  3. 上述 invalidation 后,SSE、active story/operation、analysis request generation、overlay/note、backend connection 和 session save timer 均按现有契约完成 teardown。
  4. invalidation 后打开/切换 tab、sidebar、floating panel 或 settings,不会因读取 request context 崩溃。
  5. invalidation 状态下不能发送分析请求,也不能写入旧 identity 的 analysis context/session storage。
  6. 注入一个新的有效 session 后,只恢复新 tenant/user/workspace scope 的状态。
  7. 非 OIDC local/API-key 模式的初始化、provider/codebase 请求和现有 session 行为保持不变。

测试应验证用户可观察结果和身份隔离,不要只验证某个 mock 函数“被调用”。

5. 在 Perfetto 子仓先完成源码修复和完整验证

上述两组源码修复应先提交到 Gracker/perfetto#15。至少运行:

node ui/build.mjs --typecheck --no-build --no-wasm --no-depscheck
node ui/build.mjs --run-unittests --no-build --no-wasm --no-depscheck

针对性复现可用:

ui/node_modules/.bin/vitest run \
  --config ui/vitest.config.mjs \
  ui/src/core/smartperfetto_backend_fetch_unittest.ts \
  ui/src/plugins/com.smartperfetto.AIAssistant/codebase_api_unittest.ts \
  ui/src/plugins/com.smartperfetto.AIAssistant/provider_events_unittest.ts \
  ui/src/plugins/com.smartperfetto.AIAssistant/ai_panel_navigation_unittest.ts

完成标准是:全量 UI tests 0 failure,新增 auth-expiry 场景真实通过,类型检查通过,git diff --check 通过。

Gracker/perfetto#15 当前的 enforce-reviews failure 是上游针对 fork PR 的 Google 组织 review 策略,不是本次源码问题;不要为了它篡改仓库 workflow。需要关注的功能门禁是本地全量 UI tests,以及该 PR 的 ui / uirepo-checks / repo-checks 最终结果。

6. 子仓修完后,重新同步根仓 gitlink 和生成的 frontend

根 PR 当前 gitlink 指向 3a2bb0cd7cab1461e69d5266d2e28fc5a8dd73c0。修复后必须:

  1. 让根仓 perfetto/ gitlink 指向包含上述修复的子仓提交。
  2. 不要手改根仓生成的 frontend/ 文件。
  3. 从根仓运行:
    ./scripts/update-frontend.sh
    npm run check:frontend-prebuild
  4. 确认生成的 frontend version、bundle、service worker、manifest/hash 与新的子仓提交一致。
  5. 只提交实际重新生成的产物,不要夹带本地缓存、out 目录或无关格式化。

7. 重新运行根仓验证

更新根仓 gitlink和 frontend 后,至少运行当前项目定义的门禁:

cd backend && npm run build
cd ..
npm run check:frontend-prebuild
npm run verify:pr
git diff --check

同时保留并重跑本 PR 已新增的后端回归:

  • backend/src/__tests__/runtimeEnvironment.test.ts
  • backend/src/routes/__tests__/enterpriseTraceMetadataRoutes.test.ts

当前评审中这两个 Jest suite 共 23 tests 已通过;修复 UI 后不能破坏这些后端结果。

如果本地 npm run verify:pr 仍受明确的 Rust toolchain 版本限制,请贴出精确失败阶段和版本信息,并以 GitHub 对应 runner 的成功结果补足,不能笼统写“应该通过”。

8. 做一次架构级简化复核,不要扩大修复范围

修复时请检查以下边界:

  • credentials/CSRF 策略只有一个权威 helper,避免 provider、codebase、AI panel 各自 hardcode。
  • OIDC request context 只有服务端 session 是身份真相;不得从浏览器持久化恢复身份。
  • auth invalidation teardown 与正常 trace switch/panel disposal 使用一致的 generation/cancellation 语义。
  • Viewer 的 WASM target 与 AI backend 的 scoped lease 继续保持分离。
  • enterprise_auth_card.tsanalysisbackendconnection.ts 已较大;如果新修复继续增加分支,应提取明确的 auth gate/connection state helper。但不要为了“拆文件”做无行为收益的大规模重构。
  • 只简化本次相关代码;不要顺手修改无关文件或生成物模板之外的内容。

9. 完成真实场景 QA

自动测试全部通过后,至少手工验证:

  1. OIDC 未登录时由 bootstrap gate 正常阻止进入分析页面。
  2. 登录后打开 trace,Viewer 使用 WASM,不注册/复用用户的全局 HTTP RPC target。
  3. 页面级 AI lease 从 preparing 到 ready,分析请求可以正常发起。
  4. AIPanel 打开期间执行 logout,页面不抛异常、不继续写回旧会话,并显示明确的重新登录状态。
  5. AIPanel 打开期间模拟 session 401/expiry,结果同上。
  6. 跨 tab invalidation 后,旧 tab 不继续 SSE/analysis/overlay 写回。
  7. workspace/session 切换后,不恢复旧 workspace 的 trace/session/run/lease/analysis-context。
  8. 非 OIDC local/API-key 模式下,provider switcher、provider form、codebase picker/register/delete 和普通 AI 请求继续工作,未显式指定时不强制 credentials: 'include'
  9. Docker/./start.sh 使用提交后的 frontend/,无需现场重新编译 UI。

请在 PR 评论中记录 QA 环境、实际操作、结果和仍未覆盖的边界,不要只写“手测通过”。

10. 合入顺序与最终交付证据

完成后请在本 PR 贴出:

  • Perfetto 修复提交 SHA。
  • 根仓更新后的 head SHA。
  • 全量 Perfetto UI test 摘要。
  • typecheck 摘要。
  • 根仓 npm run verify:pr 摘要。
  • npm run check:frontend-prebuild 结果。
  • GitHub CI 最终状态。
  • 手工 OIDC expiry/logout 与非 OIDC 回归结果。
  • 明确说明是否仍有 deferred 项及原因。

合入顺序必须是:

  1. 先合入 feat: 完善 SmartPerfetto OIDC 会话与分析连接隔离 perfetto#15,使修复提交从 Gracker/perfetto/main 或其祖先关系上持久可达。
  2. 如果子仓采用 squash/rebase/merge 后落地主提交发生变化,更新根仓 gitlink 到实际已落地提交,并重新生成 frontend/
  3. 确认根仓 PR feat: 完善 OIDC 页面级分析连接与会话隔离 #239 的所有检查与上面验收项通过。
  4. 最后再合入根仓 PR feat: 完善 OIDC 页面级分析连接与会话隔离 #239

在这两个功能问题修复、完整 UI tests 归零、以及子仓提交 durable landing 之前,本 PR 保持不合入。

@cipherTing

Copy link
Copy Markdown
Contributor Author

已更新 fork 分支,根仓当前提交为 9f63d776

已将子模块 gitlink 同步至 681bee7b,并使用官方脚本刷新提交版 frontend/。已通过完整 UI build、UI typecheck、check:frontend-prebuildgit diff --check

此 PR 继续依赖子仓 PR #15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants