角色(Access Roles)¶
功能群:系統管理|權限模型(三段式權限名、萬用字元規則、出貨角色)先讀 功能群總覽 §2|同組頁:角色明細、指派、檢查存取
事實基準:2026-10-08 從 FE
src/views/access/RoleList.vue等 + jedi-iamapi/routes/role_route.py、ui_route_route.py+ BEapp/auth/service/role_transfer_service.py+ 本機 DEV DB 掃出。
變更紀錄¶
| 日期 | FR | 變更 |
|---|---|---|
| 2026-10-08 | FR-132 | 新頁:存取管理「角色」列表,取代角色權限管理列表頁;含匯出/匯入角色 JSON |
1. 功能描述¶
「角色」是存取管理的第一頁(側欄 存取管理 › 角色,route access-roles,/access/roles)。角色=一組權限;管理員在這裡看全部角色、建立/複製/刪除角色、預覽某角色登入後看得到的選單,並以 JSON 匯出匯入角色(跨環境搬角色)。勾選權限在 角色明細;把角色給人在 指派。
主要使用者:租戶管理員(持 iam.role.* 權限者)。
角色速覽
| 角色 | 在本頁能做什麼 |
|---|---|
持 iam.role.read |
看列表、預覽可見選單、進明細檢視 |
另持 iam.role.create/delete/export/import |
對應出現「新增角色/複製」「刪除」「匯出」「匯入角色 JSON」 |
系統管理員(*.*.*) |
全部可做;內建系統管理員本身只能檢視、複製、匯出 |
沒有 iam.role.read |
側欄看不到本頁,直接打網址被路由守門擋到無權限頁 |
1.1 功能總覽¶
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 角色列表 | 一列一角色:名稱(內建帶鎖頭+「內建」標籤、停用帶「已停用」)、描述、租戶、權限數(展開後)、使用者數、最後修改(出貨角色未被改過顯示「系統建立」) | 主表 | §6.2 |
| 2 | 搜尋 | 依角色名稱搜尋 | 表頭 | — |
| 3 | 篩選租戶 | 只縮小顯示範圍;看得到哪些租戶仍由資料庫租戶隔離決定 | 表頭 | — |
| 4 | 顯示已停用 | 預設只列啟用中的角色 | 表頭 | — |
| 5 | 排序 | 名稱、使用者數、最後修改由後端排序;權限數只排本頁(數字由系統即時展開計算) | 欄標題 | §12 |
| 6 | 預覽權限 | 對話框列出指派此角色的人登入後可見的選單(綠點可見、灰點不可見) | 每列眼睛鈕 | §6.3 |
| 7 | 檢視/編輯 | 進 角色明細;內建系統管理員顯示「檢視」 | 每列 | — |
| 8 | 複製 | 進新增頁並以此角色為範本預填 | 每列 | UC-ROLES-01 |
| 9 | 匯出 JSON(單一) | 下載 role-<名稱>.json |
每列 | UC-ROLES-02 |
| 10 | 刪除 | 有人在用時確認框列出受影響人數;內建系統管理員無此鈕 | 每列 | §4 |
| 11 | 匯出全部 | 目前篩選條件下的全部角色(不只本頁),存成一個檔 | 頁首 | UC-ROLES-02 |
| 12 | 匯入角色 JSON | 對話框選檔、顯示檔內角色、處理零命中與同名 | 頁首 | UC-ROLES-03 |
| 13 | 新增角色 | 進新增頁 | 頁首 | UC-ROLES-01 |
展開成 UC:UC-ROLES-01(新增/複製)、UC-ROLES-02(匯出)、UC-ROLES-03(匯入)。
2. Use Case¶
角色速覽:同 §1。
視覺慣例:菱形=判斷;紅框=擋下(標錯誤碼);綠框=成功。
UC-ROLES-01 新增或複製角色¶
| 項目 | 內容 |
|---|---|
| 角色 | 持 iam.role.create |
| 前置條件 | 授權未到期(到期唯讀時按鈕停用並附原因) |
| 產出後置 | 進 角色明細 新增模式;複製時 ?template=<uid> 預選範本、名稱預填「<原名> 複本」 |
UC-ROLES-02 匯出角色¶
| 項目 | 內容 |
|---|---|
| 角色 | 持 iam.role.export |
| 前置條件 | 無 |
| 產出後置 | 下載 JSON:{"format_version":1,"catalog_version":N,"roles":[{name, description, is_admin, capabilities:[規則…]}]};規則原樣、不展開,不帶 id |
UC-ROLES-03 匯入角色¶
| 項目 | 內容 |
|---|---|
| 角色 | 持 iam.role.import |
| 前置條件 | 檔案是本系統匯出的 JSON |
| 產出後置 | 依名稱對應建立或覆蓋角色;匯入的角色一律不是內建系統管理員 |
flowchart TD
A["選擇 JSON 檔"] --> B{"檔案格式正確?"}
B -- 否 --> E1["前端提示:檔案格式不正確"]
B -- 是 --> C["POST /roles/import"]
C --> D{"每條規則在本環境至少命中一項權限?"}
D -- 否 --> E2["400 AUTH_400009<br/>整份不匯入,列出角色:規則"]
D -- 是 --> F{"有同名角色?"}
F -- "與內建系統管理員同名" --> G["一律另存成新角色"]
F -- "與一般角色同名、未選處理方式" --> E3["409 AUTH_409017<br/>對話框請使用者選覆蓋或另存"]
E3 --> C
F -- 無/已選 --> H["建立或覆蓋"]
G --> OK["成功:已匯入 N 個角色"]
H --> OK
舊版本匯出的檔(catalog_version 較舊或沒帶):規則若是某項權限的舊名,先換成新名再比對。
3. 權限矩陣¶
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 進入本頁、列表 | 側欄可見=iam.role.read(route_capabilities ALL) |
iam.role.read |
jedi-iam api/routes/role_route.py(RolesRoute.post) |
| 權限數、可見頁數 | 同上 | iam.role.read |
ui_route_route.py(RoleStatsRoute) |
| 預覽權限 | v-can iam.role.read |
iam.role.read |
ui_route_route.py(UiRoutesVisibleRoute) |
| 新增/複製 | v-can iam.role.create |
iam.role.create |
role_route.py(RoleCreateRoute) |
| 刪除 | v-can iam.role.delete;內建不顯示 |
iam.role.delete;內建回 409 |
role_route.py(RoleRoute.delete)→ role_domain_service.py(AUTH_409019) |
| 匯出 | v-can iam.role.export |
iam.role.export |
role_route.py(RoleExportRoute) |
| 匯入 | v-can iam.role.import |
iam.role.import |
role_route.py(RoleImportRoute) |
FE 與 BE 判定一致。缺權限時 BE 回 403 GRC_403022,畫面顯示「使用者不具備此操作所需的權限(需要權限:<權限名>)」。
4. 狀態機與前置條件¶
| 條件 | 效果 | 出處 |
|---|---|---|
roles.is_system=true(內建系統管理員) |
只能檢視、複製、匯出;改、停用、刪除一律 409 AUTH_409019 |
jedi-iam domain/service/role_domain_service.py |
roles.is_template=true(出貨範例) |
與自建角色相同,可改可刪可複製 | resources/roles/shipped-roles.json |
roles.enable=0 |
預設不列;開「顯示已停用」才出現並帶「已停用」標籤 | RoleList.vue(listFilters) |
| 授權到期唯讀 | 複製、刪除停用,tooltip 說明原因 | useLicenseReadonly |
| 刪除有人在用的角色 | 確認框說明「目前指派給 N 位使用者,刪除後失去此角色的權限」 | RoleList.vue(confirmDelete) |
出貨角色(乾淨安裝與升級都帶入,來源 resources/roles/shipped-roles.json):
| 角色 | 規則 | 可改 |
|---|---|---|
| 系統管理員 | *.*.* |
否,每租戶恰一個 |
| 稽核主管 | grc.*.*、oscal.*.*、asset.*.*、evidence.*.*、detection.*.*、survey.*.* 等 |
可改可刪 |
| 稽核員 | 讀全部業務資料;任務只有檢視與修改;稽核可建立、修改、匯入 | 可改可刪 |
| 一般成員 | 讀業務資料;任務檢視與修改、留言、證據 | 可改可刪 |
| 唯讀 | 逐模組列 read(不用 *.*.read,避免日後新模組自動被讀) |
可改可刪 |
四個範例角色都不給 workspace.feedback.export。
5. UI 設計¶
單頁:頁首(標題+一句說明+「匯出全部」「匯入角色 JSON」「新增角色」)、表頭工具列(搜尋、租戶篩選、顯示已停用開關)、DataTable(分頁)。對話框兩個:預覽權限(可見選單樹)、匯入角色 JSON(虛線層)。
📸 實機截圖待補(亮色模式):列表整頁、預覽權限對話框、匯入對話框(零命中清單/同名選項兩種狀態)。
介面用語:對使用者一律說「權限」「項」,不出現「能力點」「pattern」「capability」。
6. API 規格¶
6.1 總清單¶
| 分類 | Method + Path | 說明 | 守門 |
|---|---|---|---|
| 列表 | POST /api/1.0/roles |
分頁+篩選(name、tenant_id、enable、is_delete) |
iam.role.read |
| 統計 | POST /api/1.0/roles/stats |
每角色展開後權限數、可見頁數、每條規則展開幾項 | iam.role.read |
| 預覽 | POST /api/1.0/ui-routes/visible-by-capabilities |
給權限名或角色,回可見頁 | iam.role.read |
| 刪除 | DELETE /api/1.0/role/<uid> |
硬刪除 | iam.role.delete |
| 匯出 | POST /api/1.0/roles/export |
{uids:[...]} |
iam.role.export |
| 匯入 | POST /api/1.0/roles/import |
見 §6.4 | iam.role.import |
| 下拉 | GET /api/1.0/tenants/menu |
租戶篩選 | 登入即可 |
6.2 列表回應(RoleResponse)¶
| 欄位 | 型別 | 說明 |
|---|---|---|
uid / name / description |
string | |
enable |
int | 1 啟用、0 停用 |
is_admin |
int | 管理員旗標(升級前的管理員角色保留) |
is_system |
bool | 內建系統管理員 |
is_template |
bool | 出貨範例 |
tenant_id / tenant |
int / object | 所屬租戶 |
capability_patterns |
list[string] | 角色存的規則原樣 |
user_count |
int | 指派人數 |
updated_user / updated_user_name / updated_at |
審計欄位(updated_user='system'=出貨後未被改過) |
出處:jedi-iam api/serializers/role.py(RoleResponse)。
6.3 統計回應(/roles/stats)¶
Request:{"uids": [...], "tenant_id": 102}——展開結果依角色所屬租戶而定(原廠租戶多原廠權限),前端同租戶的角色一次問。
| 欄位 | 型別 | 說明 |
|---|---|---|
uid |
string | |
capability_count |
int | 規則展開後實際權限數 |
visible_page_count |
int | 可見頁數(與側欄同一條判定,含授權照過濾) |
pattern_counts |
dict | 每條規則展開幾項 |
6.4 匯入(/roles/import)¶
{
"format_version": 1,
"catalog_version": 3,
"roles": [
{"name": "稽核員", "description": "…", "is_admin": 0, "capabilities": ["grc.*.read", "grc.job.update"]}
],
"on_conflict": "save_as_new"
}
| 欄位 | 說明 |
|---|---|
on_conflict |
overwrite/save_as_new;不帶且有同名回 409 |
回應 roles |
每個匯入結果 |
| 錯誤碼 | HTTP | 情境 |
|---|---|---|
AUTH_400009 |
400 | 有規則在本環境命中零項權限;data.unmatched=[{role, pattern}],整份不匯入 |
AUTH_400010 |
400 | on_conflict 值不對 |
AUTH_400011 |
400 | 檔內沒有角色或名稱重複 |
AUTH_409017 |
409 | 與既有一般角色同名、未選處理方式 |
LICENSE_403004 |
403 | 角色含未授權模組的權限 |
出處:BE app/auth/service/role_transfer_service.py;錯誤碼 jedi-iam common/enum/error_code.py。
7. 前端檔案地圖(compliance-manager-fe/)¶
| 檔案 | 角色 |
|---|---|
src/views/access/RoleList.vue |
列表頁 |
src/views/access/RolePreviewDialog.vue |
預覽權限對話框 |
src/views/access/RoleImportDialog.vue |
匯入對話框(零命中、同名兩種回應自行呈現,不 toast) |
src/service/RoleAccessService.js |
列表、統計、匯出入 API |
src/components/access/VisibleMenuPreview.vue |
可見選單樹(預覽與明細共用) |
src/config/router/index.js |
access-roles;舊網址 /auth/role-manage、/auth/role-form 轉址到本頁與明細 |
8. 後端檔案地圖¶
| 鏈路 | Route | Service | 底層 |
|---|---|---|---|
| 列表 | jedi-iam role_route.py RolesRoute |
BE app/auth/service/role_app_service.py(補審計暱稱) |
jedi-iam role repo |
| 統計/預覽 | jedi-iam ui_route_route.py |
jedi-iam ui_route_service、capability_expansion_service |
Redis 快取(鍵含權限目錄版本號) |
| 匯出入 | jedi-iam role_route.py |
BE app/auth/service/role_transfer_service.py(展開後交 role_app_service 過授權照守門) |
jedi-iam shipped_role_writer 寫規則表 |
9. DB¶
| 表 | 讀寫 | 說明 |
|---|---|---|
public.roles |
讀/寫(刪除、匯入) | is_system、is_template、enable、tenant_id |
public.role_capability_patterns |
讀/寫(匯入) | role_id × pattern;判定唯一來源 |
public.capabilities |
讀 | 展開萬用字元、命中檢查、舊名對照(legacy_name) |
public.user_roles |
讀 | 使用者數 |
欄位說明見 角色明細 §9。
10. 頁面邏輯與資料對應¶
載入:POST /roles(分頁)→ 依租戶分組各打一次 POST /roles/stats → 併回每列 capability_count。切「顯示已停用」或租戶篩選回第一頁重載。
| 畫面元素 | API 欄位 |
|---|---|
| 名稱旁鎖頭 | is_system |
| 權限數 | stats capability_count |
| 最後修改 | updated_at + updated_user_name(updated_user='system' 顯示「系統建立」) |
11. 背景行為與外部依賴¶
| 類型 | 內容 |
|---|---|
| 快取 | 規則展開結果存 Redis,鍵含權限目錄版本號;權限目錄變動即換鍵 |
| socket/排程 | 無 |
| 套件 | jedi-iam(角色、指派、判定) |
12. 邊界情況與已知坑¶
- 權限數欄只排本頁:數字由
/roles/stats即時展開,不是資料庫欄位,無法跨頁排序;欄標題 tooltip 有說明。 - 刪除是硬刪除:刪除後該角色的指派一併失效,使用者重新登入後即失去相關權限。
- 匯入零命中整份擋下:任何一條規則在本環境命中零項就整份不匯入——靜默跳過會讓匯入後的角色比來源少權限而沒人發現。
- 匯入不碰授權照:含未授權模組權限時由角色寫入端的授權照守門擋(
LICENSE_403004)。