跳轉到

指派(Role Assignments)

功能群:系統管理|權限模型先讀 功能群總覽 §2|同組頁:角色、檢查存取(嵌在本頁頂部)

事實基準:2026-10-08 從 FE src/views/access/AssignmentManage.vue、AssignmentDialog.vue、assignmentPeriod.js + jedi-iam api/routes/role_assignment_route.py、app/service/role_assignment_service.py + 本機 DEV DB 掃出。

變更紀錄

日期 FR 變更
2026-10-08 FR-132 新頁:一筆指派=某人 × 某角色 × 範圍 × 期間;到期自動失效;頂部嵌檢查存取

1. 功能描述

指派頁(側欄 存取管理 › 指派,route access-assignments,/access/assignments)列出「誰在什麼期間擁有哪個角色」。一筆指派=使用者 × 角色 × 範圍(租戶/部門)× 起訖日。到期的指派不用手動移除:仍列在表上並標「已到期」,但權限判定與側欄選單不再採計。頂部的「檢查存取」搜尋框可以查單一使用者實際擁有的權限,見 檢查存取。

範圍:畫面一律建「整個租戶」範圍的指派。資料表與 API 保留部門範圍(scope=org_unit、org_unit_id),但部門目前只當屬性記錄,判定上與整個租戶相同——部門資料隔離本版不做。

主要使用者:租戶管理員。

角色速覽

角色 在本頁能做什麼
持 iam.user.read 看指派表、檢查存取
另持 iam.user.assign 新增、編輯期間、移除指派
沒有 iam.user.read 側欄看不到;直接進頁顯示「目前帳號沒有檢視指派的權限」

1.1 功能總覽

# 功能 說明 位置 詳述
1 檢查存取 輸入姓名或帳號,表格區切成該人的權限來源與可見選單 頁首下方 檢查存取
2 指派表 使用者、角色、生效、到期(空=永久)、狀態 主表 §6.2
3 依人/依角色 同一張表換分組列(分組列顯示筆數) 工具列 —
4 角色篩選 只看某角色;從 角色明細「檢視指派清單」進來時已預選 工具列 —
5 只看某人 網址帶 ?user_uid= 時出現可移除的「僅檢視:<姓名>」標籤 工具列 —
6 顯示已到期 預設隱藏;表尾提示「另有 N 筆已到期未顯示」 工具列 §4
7 新增指派 對話框:選人(可多選)、角色、期間;右側預覽可見選單變化 頁首 UC-ASSIGN-01
8 編輯期間 只改起訖;換人或換角色請移除後重建 每列 UC-ASSIGN-02
9 移除指派 確認後刪除;移除後重新登入就無法使用相關功能 每列 —

2. Use Case

角色速覽:同 §1。

UC-ASSIGN-01 新增指派

項目 內容
角色 持 iam.user.assign
前置條件 被指派者是角色所屬租戶的成員;一次只選同一租戶的人
產出後置 每位使用者各一筆 user_roles(scope=tenant)
flowchart TD
  A["選人(可多選)"] --> B{"都在同一租戶?"}
  B -- 否 --> X0["提示:請一次只選同一個租戶的人"]
  B -- 是 --> C["角色下拉只列該租戶角色<br/>選角色、期間(永久/指定起訖)"]
  C --> P["右側預覽:目前已可見(灰)+加上此角色後新增(綠)"]
  P --> D["建立指派:每人各送一次 POST /role-assignment"]
  D --> E{"生效日晚於到期日?"}
  E -- 是 --> X1["400 AUTH_400012"]
  E -- 否 --> F{"同人同角色同範圍期間重疊?"}
  F -- 是 --> X2["409 AUTH_409018"]
  F -- 否 --> G{"是該租戶成員?"}
  G -- 否 --> X3["400 AUTH_400014"]
  G -- 是 --> OK["已建立 N 筆指派<br/>部分失敗時列出失敗的人"]

UC-ASSIGN-02 編輯期間或移除

項目 內容
角色 持 iam.user.assign
前置條件 不會讓原廠租戶最後一位系統管理員失去有效指派
產出後置 更新起訖或刪除該筆

改完或刪完後,該人「現在仍有效」的指派要留得住原廠管理員身分,否則擋下——過期、未生效、角色停用的列不算數,避免留一筆過期指派或把唯一那筆改成過去期間來繞過保護。

3. 權限矩陣

操作 FE 判定 BE 強制 BE 檢查位置
進入、列表 側欄可見=iam.user.read(ALL);頁面 v-can 同一項 iam.user.read jedi-iam role_assignment_route.py(RoleAssignmentsRoute)
新增 v-can iam.user.assign iam.user.assign RoleAssignmentCreateRoute
編輯、移除 v-can iam.user.assign(整欄隱藏) iam.user.assign RoleAssignmentRoute.put/delete
檢查存取 同進入 查他人需 iam.user.read;查自己放行 UserEffectiveAccessRoute

4. 狀態機與前置條件

指派狀態(前端依日期推,useRoleAssignmentList.js assignmentStatus):

狀態 條件 判定採計
未生效 starts_at 在未來 否
有效 期間內且角色、帳號都啟用 是
停用 期間內但角色或帳號已停用(is_active=false) 否
已到期 ends_at 已過 否;表上預設隱藏,開「顯示已到期」才出現

期間:生效日選今天=立即生效;指定日期=當天 00:00 起。到期日=當天 23:59:59 結束;不填=永久。

生效時機:權限判定每次請求依當下有效指派計算,到期即不再放行;側欄選單與登入憑證內的管理員旗標在重新登入後更新。

條件 效果 出處
生效晚於到期 400 AUTH_400012 role_assignment_service.py(_assert_period)
同人、同角色、同範圍期間重疊 409 AUTH_409018 _assert_no_overlap
被指派者不是該租戶成員 400 AUTH_400014 _resolve_scope
角色不屬於該租戶、範圍不正確 400 AUTH_400013 _resolve_scope/_assert_role_usable_in
會讓原廠管理員失去最後一筆有效指派 403 _assert_root_admin_keeps_role
指派不存在 404 AUTH_404008

5. UI 設計

頁首(標題、說明、「新增指派」)→ 檢查存取搜尋框 → 未搜尋時:工具列(依人/依角色、角色篩選、僅檢視某人標籤、顯示已到期)+分組 DataTable+表尾總數;搜尋某人後整塊換成 檢查存取 結果。新增/編輯指派為對話框(虛線層),編輯時無預覽、角色唯讀。

📸 實機截圖待補(亮色模式):依人分組、依角色分組、新增指派對話框(含預覽)、已到期顯示。

6. API 規格

6.1 總清單

分類 Method + Path 說明 守門
列表 POST /api/1.0/role-assignments 分頁+篩選 iam.user.read
新增 POST /api/1.0/role-assignment 一次一人 iam.user.assign
編輯 PUT /api/1.0/role-assignment/<id> 只改期間 iam.user.assign
移除 DELETE /api/1.0/role-assignment/<id> iam.user.assign
檢查存取 GET /api/1.0/users/<uid>/effective-access 見 檢查存取 查他人 iam.user.read
下拉 GET /api/1.0/roles/menu/GET /api/1.0/users/menu 角色/人 iam.role.read/登入即可
預覽 POST /api/1.0/ui-routes/visible-by-capabilities 新增對話框預覽 iam.role.read

6.2 列表

Request:

{
  "pager": {"page": 1, "page_size": 25, "with_total": true},
  "sort": [{"field": "user_id", "order": "asc"}, {"field": "starts_at", "order": "asc"}],
  "filters": {"role_uid": null, "user_uid": null, "hide_expired": true}
}

篩選另可帶 org_unit_id、active_only。回應每列(RoleAssignmentResponse):

欄位 型別 說明
id int 指派 id
user_uid / user_login_name / user_nickname string
role_uid / role_name string
scope string tenant/org_unit
tenant_id / org_unit_id / org_unit_name 範圍
starts_at / ends_at datetime ends_at 空=永久
is_active bool 期間內且角色、帳號皆啟用
is_default bool 資料欄位保留,畫面不顯示

出處:jedi-iam api/serializers/role_assignment.py。

6.3 新增/編輯

{
  "user_uid": "…",
  "role_uid": "…",
  "scope": "tenant",
  "tenant_id": 102,
  "starts_at": "2026-10-08 00:00:00",
  "ends_at": "2026-12-31 23:59:59"
}

tenant_id 帶被指派者所屬租戶;不帶時後端用操作者當下租戶(指派子租戶的人會被擋)。編輯只收 scope、starts_at、ends_at。

7. 前端檔案地圖(compliance-manager-fe/)

檔案 角色
src/views/access/AssignmentManage.vue 頁面本體
src/views/access/AssignmentDialog.vue 新增/編輯對話框(帳號頁、角色明細共用)
src/views/access/AssignmentFormBody.vue、AssignmentMenuPreview.vue、assignmentPeriod.js 角色+期間表單、預覽、期間換算
src/views/access/CheckAccessBar.vue、EffectiveAccessView.vue 檢查存取
src/composables/useRoleAssignmentList.js 列表與狀態推算
src/service/RoleAssignmentService.js API

8. 後端檔案地圖

鏈路 Route Service 底層
指派 CRUD jedi-iam api/routes/role_assignment_route.py jedi-iam app/service/role_assignment_service.py user_role repo;原廠管理員保護由宿主 root admin guard 判

9. DB

表 讀寫 說明
public.user_roles 讀/寫 指派本體
public.roles、public.users、public.user_tenants 讀 角色、人、租戶成員檢查

public.user_roles

欄位 型別 說明
id bigint
user_id / role_id integer
scope varchar tenant/org_unit
tenant_id / org_unit_id integer 範圍
starts_at / ends_at timestamptz 有效期間;ends_at 空=永久
is_default boolean 保留,判定與畫面不使用

10. 頁面邏輯與資料對應

列表依分組欄先排序再分頁(分組列才會連續);「顯示已到期」關閉時另數一次總數,算出隱藏筆數。網址參數 role_uid(角色篩選)、user_uid(只看某人)、check(檢查存取對象)可直接分享。

11. 背景行為與外部依賴

類型 內容
排程 無——到期由判定當下比對日期,不需要排程清除
通知 無
套件 jedi-iam

12. 邊界情況與已知坑

  1. 一次只能指派同一租戶的人:角色屬於租戶,跨租戶多選時提示並擋下。
  2. 編輯不能換人換角色:要換請移除後重建(後端不收)。
  3. 部門範圍目前等同整租戶:API 收 scope=org_unit 並記下部門,但判定不看部門;畫面不送部門。
  4. 移除後要重新登入側欄才更新:API 判定立即生效,側欄選單與登入憑證內的管理員旗標在重新登入後才更新。

13. 開發與驗證