C8 — _ROLE_LABEL_MAP 清理 + Role ID 修正

級別:小 依賴:C1(system_menus 已 seed,提供權威 role list) 被依賴:B 階段匯出 + C2 SSP party endpoint


1. 目標

清理 SSP 匯出 generator 內 hardcoded 的 role label map,移除錯誤混入的專案角色(manager/auditor/viewer),修正 security-officer vs system-security-officer 對不上的 bug。

2. 範圍

In scope

檔案 修正項
app/oscal/service/export/ssp_docx_generator.py:32 _ROLE_LABEL_MAP 移除 manager / auditor / viewer;修 security-officersystem-security-officer;補齊 C1 9 個角色
domain/oscal/adapter/cmmc_ssp_adapter.py:49 _ROLE_LABEL_PATTERNS 擴增到 9 個角色對應的中/英文 regex pattern

Out of scope

  • system_menus master data(已在 C1 處理)
  • FE dropdown 改 API(屬 C2)
  • Excel template sheet_definitions.py:138 role enum(屬 C2/C5 整合改造)

3. 改動詳情

3.1 ssp_docx_generator.py_ROLE_LABEL_MAP

改動前(10 條,含 3 條錯誤):

_ROLE_LABEL_MAP: dict[str, str] = {
    "responsible-organization": "Responsible Organization",
    "information-owner":        "Information Owner",
    "information-provider":     "Information Provider",
    "information-receiver":     "Information Receiver",
    "system-owner":             "System Owner",
    "security-officer":         "Security Officer",       # ⚠️ ID 對不上 adapter
    "manager":                  "Manager",                 # ⚠️ 專案角色不該出現
    "auditor":                  "Auditor",                 # ⚠️ 專案角色不該出現
    "viewer":                   "Viewer",                  # ⚠️ 專案角色不該出現
}

改動後(9 條,與 C1 master 對齊):

# 對齊 C1 system_menus group='ssp_party_role' 9 個 OSCAL 標準角色
# 此 map 僅用於 SSP docx 匯出時將 role_id 轉成英文 label(OSCAL 文件慣例)
_ROLE_LABEL_MAP: dict[str, str] = {
    "responsible-organization": "Responsible Organization",
    "system-owner":             "System Owner",
    "system-security-officer":  "System Security Officer",
    "authorizing-official":     "Authorizing Official",
    "information-owner":        "Information Owner",
    "information-provider":     "Information Provider",
    "information-receiver":     "Information Receiver",
    "prepared-by":              "Prepared By",
    "prepared-for":             "Prepared For",
}

Fallback 行為:未在 map 內的 role_id 仍直接用 role_id 當 label 顯示(既有行為不變)。

3.2 cmmc_ssp_adapter.py_ROLE_LABEL_PATTERNS

改動前(5 條):

_ROLE_LABEL_PATTERNS = [
    ("responsible-organization", re.compile(r"Responsible Organization|專案負責單位", re.I)),
    ("information-provider", re.compile(r"Information provider|資料提供者|上游廠商", re.I)),
    ("information-receiver", re.compile(r"Information receiver|資料接收者|專案窗口", re.I)),
    ("system-owner", re.compile(r"System Owner|系統所有者", re.I)),
    ("system-security-officer", re.compile(r"System Security Officer|系統安全官", re.I)),
]

改動後(9 條,含新增 4 個 OSCAL 標準角色 pattern):

_ROLE_LABEL_PATTERNS = [
    ("responsible-organization", re.compile(r"Responsible Organization|專案負責單位", re.I)),
    ("system-owner",             re.compile(r"System Owner|系統所有者", re.I)),
    ("system-security-officer",  re.compile(r"System Security Officer|ISSO|系統安全官", re.I)),
    ("authorizing-official",     re.compile(r"Authorizing Official|授權機關|AO", re.I)),
    ("information-owner",        re.compile(r"Information Owner|資料所有者", re.I)),
    ("information-provider",     re.compile(r"Information Provider|資料提供者|上游廠商", re.I)),
    ("information-receiver",     re.compile(r"Information Receiver|資料接收者|專案窗口", re.I)),
    ("prepared-by",              re.compile(r"Prepared By|文件撰寫者", re.I)),
    ("prepared-for",             re.compile(r"Prepared For|文件對象", re.I)),
]

_ORGANIZATION_ROLES = {"responsible-organization", "prepared-for"}  # 補 prepared-for

注意

  • 順序保留語意(一般 docx 出現順序)
  • regex pattern 維持 re.I (case-insensitive)
  • _ORGANIZATION_ROLESprepared-for(亞航文件慣例下這個是組織單位)

4. 影響面

4.1 SSP docx 匯出(B 階段功能)

  • 既有 SSP 內若有 role_id='security-officer' 的歷史資料 → 不再有對應 label,匯出時直接顯示 security-officer 字串
  • 建議:跑一次 data audit SQL 確認既有資料有無此 role:
    SELECT DISTINCT role_id, COUNT(*)
    FROM oscal.oscal_responsible_parties
    WHERE role_id = 'security-officer'
    GROUP BY role_id;
  • 若有資料:寫一次性 SQL 把 security-officersystem-security-officer(design 預設此 case 為零,pre-flight 驗證後再決定)

4.2 CMMC SSP docx 匯入

  • 新版 adapter 可辨識更多角色 pattern → 解析覆蓋率提升
  • 既有解析過的 SSP 不受影響(已存進 DB)

4.3 既有 test fixtures

  • tests/test_cmmc_ssp_adapter.py 可能有 hardcoded role list assertion,需同步更新
  • tests/test_ssp_docx_import_diff_response.py 同上
  • tests/test_ssp_write_strategy_v2_parties.py 同上

5. 邊界條件

情境 行為
既有 SSP 用 security-officer 角色 匯出顯示 raw key(不會 fall through 到舊 label)
docx 內出現未識別的中英文 role pattern 解析時 role=None(既有 fallback 不變)
同一個 party 在 docx 內被標記多個 role 沿用 adapter 既有行為(取第一個)

6. 待 user 拍板的小決策

編號 問題 我建議
C8-D1 DB 內若有 role_id='security-officer' 的歷史資料,要不要寫一次性 SQL 改成 system-security-officer (不留 broken role_id),但 pre-flight 先 audit 數量
C8-D2 _ORGANIZATION_ROLES 是否確定 prepared-for 算 organization?亞航文件用法是 person 還是 org? 看亞航 reference docx 內 prepared-for 區塊,pre-flight 驗證

7. 開發後狀態

  • _ROLE_LABEL_MAP 9 條乾淨對齊 OSCAL 標準
  • _ROLE_LABEL_PATTERNS 擴增到 9 個角色
  • 既有 test fixtures 更新
  • (optional)一次性 SQL 修 broken role_id 資料