跳至主要内容

ADR-021: Consent-based impersonation 與 break-glass 分流

ADR 編號: 021 狀態: 已接受 (Accepted) 決策日期: 2026-07-21 決策者: Development Team 取代: 無(延伸 ADR-013 的目標控制面) 被取代: 無 修訂: 2026-08-14——緊急能力改為獨立 break-glass 端點;普通端點不得繞過 consent,並補 refresh-time consent revalidation(見文末修訂紀錄)


摘要

普通模擬端點採同意路或結構路:目標經 email 簽章連結登入後批准,或目標未命中受保護角色/帳號規則且通過 authority 子集反提權政策。緊急越權能力改走獨立 break-glass 端點,要求 impersonation:break-glass、近期 MFA step-up、理由、案件編號與短效 Session;不得在普通端點自動繞過 consent。Consent 與 Session family 綁定,撤銷/到期後不得 refresh。


背景 (Context)

需求(2026-07-21 討論定案)

  1. 超級使用者角色需要直接模擬任何人的權限。
  2. 某些角色不能被直接模擬——即使操作者具備 impersonation:enter
  3. Consent-based impersonation 適用任何人:任何目標(含受保護角色)經目標本人同意即可被模擬;同意經 email 連結取得,且因客服信箱聯絡是非同步的,連結流程需容忍天級的延遲。

既有機制與缺口

impersonation:enter 閘門管「誰可以模擬」;ImpersonationPolicy SPI+反提權預設(見 impersonation feature)管「可以模擬誰」的結構安全。缺的是:越權能力層(需求 1)、目標族群保護(需求 2——保護理由是隱私/法遵,與權限大小正交)、同意工作流(需求 3)。

先前討論已否決 per-role authority<role>:impersonation)形狀:與 authority 靜態宣告模型相衝(CUSTOM 角色動態增長)、多角色目標語意懸空、預設姿態兩難。

與 ADR-013 兩概念的關係

概念發起者語意
Impersonation管理者我變成你(無需你知情)
Delegation被代理者我授權你代理我
Consent-based impersonation(本 ADR)管理者發起、目標批准我請求變成你、你點頭後才行

本質是「請求驅動的授與、消費在 impersonation 的政策閘門」。不重用 delegation 實作——delegation 的 acting 維度(grantors)語意不同;consent 只是 enter() 的放行條件,模擬語意不變(成為目標本人)。


決策 (Decision)

D-A:普通路與緊急路分流(2026-08-14 修訂)

硬排除(不可協商):服務帳號、自己 ──────────────→ 拒絕
(tenant 模式)跨租戶且無 cross-tenant ─────────→ 拒絕

① 同意路:存在有效 consent(GRANTED、使用窗內)─→ 放行(受保護角色亦可)
② 結構路:目標無受保護角色/帳號 且 目標 authorities ⊆ actor authorities ─→ 放行

其餘 → 拒絕(403)

獨立 POST /impersonation/break-glass:
break-glass authority + 近期 MFA + 理由 + 案件編號 ─→ 短效 non-remembered family

普通路絕不因持有 break-glass authority 而繞過 consent。candidates() 同樣要求 impersonation:enter 並套用普通路政策。可被 break-glass 模擬的受保護 stable subject 不出現在普通候選名單。

D-B:詞彙與受保護角色宣告

authority語意參考連結
impersonation:enter基礎閘門(既有,不動)管理角色
impersonation:break-glass僅供獨立緊急端點越過普通政策僅緊急操作角色;搭配 MFA step-up
impersonation:cross-tenant租戶邊界(既有,正交不動)

受保護角色與帳號集合=設定宣告app.impersonation.protected-rolesapp.impersonation.protected-subjects。角色可經有效 consent 放行;stable subject 只能走 break-glass。

D-C:同意記錄權威、簽章連結只是運輸+登入捷徑

兩個效期分離,且同意授權以 consent 記錄為準

效期管什麼預設設定
請求效期請求後多久內目標可批准7 天app.impersonation.consent.request-ttl
使用窗批准後多久內可模擬48 小時app.impersonation.consent.grant-ttl

email 內的簽章連結走既有 createEventActionLink(REUSABLE、consume 即免密登入目標本人、導向同意頁),效期沿用既有 app.security.signed-link.event-link-ttl-hours(預設 48h)。連結逾期不等於請求失效——目標仍可正常登入、在站內批准(記錄效期 7 天權威);要 email 連結全程有效,調既有設定即可,零新機制。這正是需求 3「非同步、天級延遲」的承接方式。

狀態機:PENDING →(批准)GRANTED →(逾窗)失效/(目標)REVOKED;PENDING →(拒絕)DENIED/(逾期)EXPIRED

批准後建立 impersonation family 時,Refresh Session 必須保存 consent id、grant 到期時間與 actor 必要 authority。每次 refresh 以該 id 精確重驗 requester/target/status/有效期;目標撤銷 consent 時,同一交易另依 policy reference 主動撤銷全部既有 family。兩條機制互補:主動撤銷縮短 暴露窗,refresh-time revalidation 則是跨節點與失敗重試下的最終安全線。

D-D:四個拍板

決定理由
同意綁定綁發起請求者requester × target 精確配對)最小意外;輪班交接需求出現時再放寬為設定
使用語意使用窗內多次支援會談常需反覆進出;窗短已限縮暴露面
同意身分強度需登入(簽章連結 consume 即登入目標本人,批准是 authenticated 呼叫、service 驗證操作者=目標)受保護角色值得保護,同意的身分強度不該只是「信箱持有」
緊急命名impersonation:break-glass清楚表達需額外控制的緊急程序,不是普通端點的 unrestricted bypass

D-E:落地為 optional 組合 feature(歷史決策,已由 2026-07-21 與 2026-08-14 修訂)

impersonation-consent(requires authimpersonationsigned-linknotification;notification 為 beanRequires——經 jar 的 NotificationEvent 發布、無 app 層 import 邊):

  • consent entity/repository/service/controller(請求、批准、拒絕、撤回、我的請求/待批清單)
  • email 通知:顯式收件人+deepLink(passwordless 先例);範本走訊息束 notification.impersonation-consent-request.email.*
  • ConsentAwareImpersonationPolicy bean 實作整個格架——bean 存在即覆蓋反提權預設(SPI 既有機制),impersonation core slice 一行不改;未裝本 feature 的專案維持現行為
  • impersonation:break-glass 由本 feature 的 AuthorityContributor 宣告;seed 的 revokedAuthorities 會從預設管理角色移除舊 impersonation:unrestricted 與先前誤配的 impersonation:break-glass
  • reference seed 不把 break-glass 指派給一般/SUPER_ADMIN 角色;只有企業 IdP 能提供 amrauth_time 的專用 operations 角色才應取得此 authority
  • 全程 audit:IMPERSONATION_CONSENT_REQUESTED/GRANTED/DENIED/REVOKED

考量的方案 (Alternatives)

方案不採理由
per-role authority(<role>:impersonation詞彙隨角色動態增長、多角色語意懸空、預設姿態兩難(先前討論已否決)
重用 delegation 實作同意acting 維度語意不同(代理 vs 成為);consent 只是 enter 的放行條件,不該改變模擬語意
同意免登入(信箱持有即同意)受保護角色的同意不該用最弱身分強度;簽章連結本就能免密登入,「需登入」零額外成本
consent 連結自帶長效期(新 purpose+自訂 consume)需複製 consume→session 中段或改 seam API;記錄權威+既有 event link 達成同等效果、零新機制
把格架塞進 impersonation core拉入 notification/signed-link 依賴,required 化了本該 optional 的組合;SPI 讓 core 零改動。(本列同日被修訂推翻——依賴前提經 fleet 檢視不成立,見「修訂紀錄」)

後果 (Consequences)

正面

  • 三種真實營運姿態(超級使用者、受保護族群、非同步同意)一次補齊;未裝者行為不變。
  • 全部用既有積木;impersonation core 與既有下游零受影響。

負面 / 成本

  • 同意流的 UI(同意頁、待批清單、發起請求入口)屬前端工作,另走 US 流程。
  • ict 的 SignedLinkAppService 為 localSeam(已客製)——sync 本 feature 時須確認其 API 含 createEventActionLink(見「誠實的成本」family)。

中性

  • 受保護角色集合是產品政策(per-project 設定),方法論不給預設名單。

2026-07-21 落地檢查清單(歷史快照)

以下項目記錄當時交付狀態;其中 unrestricted 路徑已由 2026-08-14 修訂取代,不代表目前契約。

  • feature/impersonationconsent/:entity+repository+service(Clock 注入)+controller+ConsentAwareImpersonationPolicy+authority 常數與 SeedProvider+properties
  • config/feature-impersonation-consent.ymlspring.config.import 登錄(ConfigFragmentsTest 守衛)
  • 訊息束範本(messages / zh_TW / en)
  • Role.json:SUPER_ADMIN + impersonation:unrestricted
  • catalog 登錄(requires+beanRequires: notification);I3 PASSED
  • 格架 policy 單元測試(4 案)+consent 流程 IT(5 案:受保護角色 direct 403、請求冪等、批准後窗內多次 200、撤回/拒絕後 403、非目標裁決 403、已裁決再批 409、unrestricted 直通)
  • app-server 全量 build+完整 I4(ownership 組裝樹)綠;本 ADR 定案(Proposed → Accepted)
  • llms.txt

拍板紀錄(2026-07-21)

維護者提出三條需求(超級使用者直接模擬、受保護角色、consent 適用任何人+長效連結)並拍板四個設計題:綁請求者、使用窗內多次、同意需登入、命名 impersonation:unrestricted。實作過程一項工程約束記錄在案:ImpersonationConsentIT 刻意加入共用 test context(執行期切換 properties bean、測後還原)而非 @DynamicPropertySource 另開 context——測試 JVM 的 Ehcache off-heap 預算(MaxDirectMemorySize=2g)已無另開 context 的餘裕,且開發機記憶體有限(見 app-server build.gradle.kts 註解)。


修訂紀錄(2026-07-21):consent 併入 impersonation feature

改判:廢除獨立的 impersonation-consent feature,consent 機制(entity/repository/service/controller、ConsentAwareImpersonationPolicyimpersonation:unrestricted seed、TTL 設定)併入 impersonation feature 單一出貨impersonation 的 requires 變為 actingauthnotificationsigned-linknotification 為 beanRequires)。

理由(維護者定性 + fleet 檢視):

  1. Consent 是模擬的正確設計,不是加值選配——無同意的模擬(結構路)保護權限結構、不保護隱私,對資安/法遵而言是權宜路徑。單獨出貨的 base impersonation 形狀,會誘使下游「因資安疑慮不裝、或鎖死到難用」,反而傷害採用。合併使完整三路格架成為唯一出貨形狀:consent 的可用性普遍保證,嚴格程度(受保護角色集合、實務上走哪條路)仍為 per-project 政策。
  2. D-E 原本的依賴重量論證前提不成立——notificationsigned-link 是下游優先採用的 feature,選到 impersonation 時幾乎必已在場;且 requires 強制的是 code slice 在場、非 mail 設定生效,無 mail 環境仍可安裝(consent 路休眠、結構路照常)。
  3. 遷移窗口最低點——改判當下受 catalog 治理的下游僅 ict-server 一個(且未裝 consent),合併成本為歷史最低。

保持不變:三路格架語意(D-A)、詞彙(D-B)、記錄權威與效期(D-C)、四個拍板(D-D);SPI 仍存在(下游仍可自訂 policy 覆蓋)。

落地feature/impersonationconsent/ package 併入 feature/impersonation/;config fragment 更名 feature-impersonation.yml;catalog 條目合併;I3/build/I4(ownership)全綠。

連帶修正(首次 fleet sync〔ict〕發現,同日):同意深連結原以 email+attributes Map 簽發,違反 ADR-018 自己的身分模型(呼叫端已握有 Account 卻走 email 弱鍵、多命中時降級不發連結),且 Map.of("consentId") 是死資料——consume→exchange 只傳 access/refresh token,連結 attributes 到不了前端。修正:SignedLinkAppService(參考 seam)補 username 鍵進入點 createEventActionLink(username, target, app);consent 深連結改以 username 簽發、consentId 掛 target query param、同意頁前端由 app.impersonation.consent.app 宣告(多前端專案;缺省=預設前端)。此簽章恰與 ict 既有 localSeam 一致——下游客製先一步走到 ADR-018 方向,核心反向對齊(reflow 語意)。

修訂紀錄(2026-08-14):break-glass 分流與 Session policy 綁定

舊能力路 impersonation:unrestricted 會讓普通端點在缺少額外身分證明、理由與案件脈絡時直接 越過 consent,無法證明是正常客服操作還是緊急事件,故改判為獨立 impersonation:break-glass 端點。Reference adapter 驗證 IdP JWT 的 amrauth_time;local login 沒有 step-up 證明時 fail closed。Break-glass family 固定 non-remembered、短 TTL 與絕對期限。

同時把 consent 從「只在 enter 檢查」提升為 Session family policy:框架保存 policy binding, refresh 重驗 actor/subject 狀態、必要 authority、絕對期限及 app hook;consent revoke 依 id 主動 撤銷 family。候選端點也改以 impersonation:enter 正式保護。第一版 reference 實作固定 pure impersonation,不繼承目標 delegation,並明確禁止 nested impersonation。

Reference break-glass 僅提供 server API,不提供 local-login demo UI/MSW handler:近期 MFA 必須由 企業 IdP 完成,前端也必須先走該 IdP 的 step-up journey,不能以表單或 mock 宣稱完成 MFA。 首次成功進入會把案件登錄為 OPEN;結案端點把案件持久改為 CLOSED、依 case number 撤銷全部 family,refresh 亦逐次重驗案件仍為 OPEN。正式產品應依 ticket system retarget 本地 reference registry。


相關

  • ADR-013——模擬/委派的基礎機制;本 ADR 是其目標控制面的延伸
  • impersonation feature 的 ImpersonationPolicy SPI 與 AntiEscalationImpersonationPolicy(結構路預設)
  • signed-link feature(createEventActionLink:consume 即登入的事件深連結);notification feature(顯式收件人 email)
  • 19-env-config.md——TTL 與受保護角色的設定治理