跨端: fix(客户端) 换身份必须清全部账号数据 + 鸿蒙管理台门禁改三态

两份审查报告(`docs/reviews/electron-gui-review.md` /
`harmony-client-review.md`)里两条**数据隔离**缺陷。

## ① 换身份不清数据 ⇒ 在新账号的界面下显示旧账号的邮件

`setActive` 之后 `api/config` 单例里的 API_BASE 与 bearer 就翻到了新账号,
于是此后每个请求都带**新账号**的凭证。而各 store 里还留着**旧**账号的:
  · mailStore.sent / currentMail
  · sessionStore.sessions / currentSession / currentSessionMails
  · contactStore.contacts / archivedContacts

⇒ 肉眼完全看不出来(不报错、不空屏),而**从旧视图发出的写操作**
(归档 / 转发 / 批准权限)改的是**新账号**。

新增 `src/lib/resetAccountData.ts` —— **一处实现,三个入口都调它**:

    ① 主动切账号(AccountSwitcher.pick)
    ② 登出(authStore.logout)
    ③ 任意接口 401(api/client.ts 的 unauthorized 回调 → markAnonymous)

只在 ① 里清是最容易漏的那种做法:② 和 ③ 各自还会重新泄露一次,
而它们都不在切换账号的代码路径上,grep 也找不到。
**身份变化有三条路径,清空也该有三条。**

★ 只碰**数据** store;`uiStore` 的 reset 仍由 App.tsx 负责
  (它还要复位窄屏分栏、写信态那些纯界面状态)。

判据:`test/stores/resetAccountData.test.ts`(4 格)。

## ② 鸿蒙管理台门禁**失败开放**(fail-open)

`AdminUsersPage.ets` 原来的条件是 `roleKnown && !this.isAdmin`,
于是 `roleKnown === false`(loadRole() 失败、**身份还没读到**)
落进 else 分支 ⇒ **把完整管理台整个渲染出来**。
一次网络抖动 = 管理入口对所有人可见。

讽刺的是该文件自己的头注释写的就是正确规则
(「不能把读不到当成是管理员」)—— 代码做的正是这条注释禁止的事。

⇒ 改三态:`!roleKnown` 显示「正在确认身份…」、`!isAdmin` 显示墙、
  否则管理台。

服务端 `middleware/user.go` 的 `AdminOnly` 仍在,所以**不是越权**;
但非管理员会看到完整用户列表、建号表单、改密入口 ——
属于客户端信息泄露 + 无意义的失败请求风暴。
This commit is contained in:
2026-09-28 08:27:01 +08:00
parent 044a664cc3
commit 18b148e476
28 changed files with 690 additions and 64 deletions

View File

@ -522,8 +522,18 @@ test('★ 设备:真实壁纸在设备上的尺寸/体积与压缩决策的输
}
/* 素材在设备上吗?不在就显式跳过(不假装通过) */
const listed = (D.shellOn(hdc, 'ls -la /data/local/tmp/real-wallpaper.jpg').stdout || '').trim();
if (!listed.includes('real-wallpaper.jpg')) {
const probe = D.shellOn(hdc, 'ls -la /data/local/tmp/real-wallpaper.jpg');
const listed = (probe.stdout || '').trim();
/*
* ★ 判"在不在"必须看**退出码**,不能只看输出里有没有那个文件名。
*
* `hdc shell` 会把 stderr 并进 stdout,于是文件不存在时 stdout 是
* `ls: /data/local/tmp/real-wallpaper.jpg: No such file or directory` ——
* **里面仍然含有文件名**,于是原来的 `listed.includes('real-wallpaper.jpg')`
* 判成"在",本该跳过的判据继续往下跑,撞在 `/bin/file` 上**报错**。
* 设备上没有素材是正常状态(没传过壁纸的设备),不该让整条判据变红。
*/
if (probe.status !== 0 || !/^-.*\breal-wallpaper\.jpg$/m.test(listed)) {
return t.skip('设备上没有真实素材 /data/local/tmp/real-wallpaper.jpg —— 本次不跑');
}