跨端: 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:
37
client/electron/src/lib/resetAccountData.ts
Normal file
37
client/electron/src/lib/resetAccountData.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 换身份时清空全部账号相关数据 —— **一处实现,三个入口都调它**。
|
||||
*
|
||||
* # 为什么需要这个文件
|
||||
*
|
||||
* `setActive` 之后 `api/config` 单例里的 `API_BASE` 与 bearer 就翻到了新账号,
|
||||
* 于是**此后每一个请求都带新账号的凭证**。而各 store 里还留着**旧**账号的数据:
|
||||
*
|
||||
* · `mailStore.sent` / `currentMail`
|
||||
* · `sessionStore.sessions` / `currentSession` / `currentSessionMails`
|
||||
* · `contactStore.contacts` / `archivedContacts`
|
||||
*
|
||||
* 结果就是"在新账号的界面下显示旧账号的邮件",而且**从旧视图发出的写操作
|
||||
* (归档 / 转发 / 批准权限)改的是新账号**。这类问题不报错、不空屏、
|
||||
* 肉眼完全看不出来 —— 只能靠一处收口来堵。
|
||||
*
|
||||
* # 为什么是三个入口而不是一个
|
||||
*
|
||||
* ① 主动切账号(`AccountSwitcher.pick`)
|
||||
* ② 登出(`authStore.logout`)
|
||||
* ③ 任意接口 401(`api/client.ts` 的 unauthorized 回调)
|
||||
*
|
||||
* 只在 ① 里清是最容易漏的那种做法:② 和 ③ 各自还会重新泄露一次,而它们都不在
|
||||
* 切换账号的代码路径上,grep 也找不到。**身份变化有三条路径,清空也该有三条。**
|
||||
*
|
||||
* ★ 这里只碰**数据** store;`uiStore` 的 `reset` 由 `App.tsx` 负责
|
||||
* (它还要复位窄屏分栏、写信态那些纯界面状态)。
|
||||
*/
|
||||
import { useContactStore } from '../stores/contactStore';
|
||||
import { useMailStore } from '../stores/mailStore';
|
||||
import { useSessionStore } from '../stores/sessionStore';
|
||||
|
||||
export function resetAccountData(): void {
|
||||
useMailStore.getState().resetAll();
|
||||
useSessionStore.getState().resetAll();
|
||||
useContactStore.getState().resetAll();
|
||||
}
|
||||
Reference in New Issue
Block a user