C7 — 06_外部利用服務 sheet 恢復(可選)

級別:小(兩行修改 + sheet 顯示) 依賴:無 被依賴:無 可選性:可不做(C 階段尾聲評估),不做不影響其他 phase


1. 目標

把 v2.0.0(2026-05-21)從 Excel 樣板隱藏的 06_外部利用服務 sheet 恢復,讓 user 能透過 Excel 匯入「外部利用服務 / Leveraged Services」資料到 SSP。

2. 為什麼當初被隱藏

sheet_definitions.py:255-258 註解原文:

v2.0.0 (2026-05-21):06_外部利用服務 暫時從 ALL_SHEETS 隱藏(系統內無對應
資料來源;party / date_authorized picker 拿掉後沒有 user 能填的鉤稽路徑)。
SHEET_LEVERAGED 定義保留 — 未來有需求時加回 ALL_SHEETS + 還原 parser 對應段
即可,不必重寫 SheetDef。

當時 A4/A5 階段決策:

  • 系統內無「FedRAMP authorized service catalog」master data → user 純打字 UX 差
  • party picker 拿掉後無鉤稽路徑 → 跟「直接在 SSP UI 編」沒差別

3. 為什麼現在可恢復

  • Phase C 走向「SSP 在專案內直接編輯」,純文字輸入到 SSP 是合理的(編輯頁本就是直填)
  • Excel 匯入是另一種輸入路徑,跟 SSP UI 編輯並存
  • 既有後端全 stack 仍存在(DB / write strategy / DI / handler)
  • 兩行修改即可恢復

4. 範圍

In scope

檔案 改動
app/module_frame/excel_template/sheet_definitions.py:259 ALL_SHEETS tuple 內加回 SHEET_LEVERAGED(位置:SHEET_INFO_SYSTEMS 後,SHEET_CONTROLS 前)
app/oscal/service/excel_parser/parser.py:70 leveraged=[] 改回呼叫 sh.parse_leveraged_sheet(sh.get_ws(wb, sh.SHEET_NAMES["leveraged"]), validation_errors)

Out of scope

  • SHEET_LEVERAGED 欄位調整(沿用 v2.0 精簡版 3 欄:service_name / provider / purpose)
  • DB schema / write strategy / DI 不動
  • master data(FedRAMP catalog)不引入

5. 改動詳情

5.1 sheet_definitions.py

改動前

ALL_SHEETS: tuple[SheetDef, ...] = (
    SHEET_METADATA,
    SHEET_ORGS,
    SHEET_PERSONS,
    SHEET_DEVICES,
    SHEET_INFO_SYSTEMS,
    SHEET_CONTROLS,
    SHEET_REF_DOCS,
)

改動後

ALL_SHEETS: tuple[SheetDef, ...] = (
    SHEET_METADATA,
    SHEET_ORGS,
    SHEET_PERSONS,
    SHEET_DEVICES,
    SHEET_INFO_SYSTEMS,
    SHEET_LEVERAGED,   # C7 (2026-MM-DD) 恢復:純文字輸入路徑
    SHEET_CONTROLS,
    SHEET_REF_DOCS,
)

5.2 parser.py

改動前

# 06_外部利用服務 (SHEET_LEVERAGED) 自 v2.0.0 從 ALL_SHEETS 隱藏 —
# 樣板不產出此 sheet,parser 也跳過讀取以免噴 SHEET_MISSING 警告。
# ParsedExcel.leveraged 留 default_factory=list,下游空 list 安全。
leveraged=[],

改動後

# C7 (2026-MM-DD) 恢復讀取 — 純文字 service_name / provider / purpose
leveraged=sh.parse_leveraged_sheet(
    sh.get_ws(wb, sh.SHEET_NAMES["leveraged"]), validation_errors,
),

5.3 樣板版本 bump

sheet_definitions.py:TEMPLATE_VERSION 或對應位置:

  • v2.0.0v2.1.0(minor bump,因為 sheet 數量改變屬 schema 變動)
  • A2 parser 內既有「相容性 anchor」要確認能處理 v2.0.0 + v2.1.0 兩個版本(pre-flight 確認

6. UX 設計

06_外部利用服務 sheet 欄位(沿用 v2.0 精簡版)

欄位 必填 寬度 說明
service_name 30 服務名稱(純文字,如 "AWS S3" / "Microsoft 365")
provider 30 服務提供者(純文字,如 "Amazon Web Services" / "Microsoft")
purpose 40 用途說明

設計選擇

  • 不引入下拉選單(無 master data)
  • 不引入 party picker(v2.0 已拿掉)
  • 不引入 date_authorized(v2.0 已拿掉)
  • 純文字輸入,匯入後寫到 ssp_system_implementation_items.implementation_type='leveraged-authorization'

匯入後資料路徑

Excel 06 sheet
  ↓
parser → ParsedLeveraged(service_name, provider, purpose)
  ↓
LeveragedWriteStrategy._parsed_to_item_payload
  ↓
ssp_system_implementation_items (
    name=service_name,
    title=provider,
    purpose=purpose,
    implementation_type='leveraged-authorization',
    party_uuid=None,                  # 無 picker
    date_authorized=None,             # 無 picker
    scope_type='ssp' / 'module_frame',
    scope_id=...
)

7. 邊界條件

情境 行為
Excel 06 sheet 完全空白 parser 回空 list,下游不寫資料
service_name 缺 (必填) parser 報 validation error,user 修正後重匯
既有 v2.0.0 樣板被匯入 (無 06 sheet) parser 容錯:找不到 sheet → validation_errors 加 non-blocking SHEET_MISSING + leveraged=[]
Re-import update flow A5 bugfix 已修「整份覆蓋」語義 (base.py write() delete-before-insert) — leveraged 同 SSP + 同 kind 自動覆蓋

8. 待 user 拍板的小決策

編號 問題 我建議
C7-D1 樣板版本 v2.0.0 → v2.1.0?影響 A2 parser 相容性檢查 要 bump,pre-flight 確認 parser 對舊版本 (v2.0.0) 不會強制報錯
C7-D2 此 phase 是否要做?或留到 C 階段尾聲再評估 建議做,兩行改動成本極低;但你若想先聚焦 C3 也可 defer

9. 開發後狀態

  • Excel 樣板下載含 06_外部利用服務 sheet(純文字三欄)
  • Excel 匯入流程處理 06 sheet 資料寫進 SSP items
  • 樣板版本 v2.1.0
  • C2 SSP-scoped excel-import endpoint 自動也支援(共用 parser)

10. Implementation Reality / Reconciliation(2026-05-23 Phase C 結構化欄位)

C7 first ship (commit e7f29e3, 2026-05-22) 走「純文字 3 欄」精簡版(service_name / provider / purpose)。Phase C 同期 user 反饋這結構不夠(CMMC / FedRAMP reference doc 有多種 service 類別 + 服務狀態欄位),加上 SSP UI 編輯也需要結構化欄位(不是只有 Excel)。

走「方案 B」:minimal schema extension — 加 2 個新欄位 + 補 2 個既有 OSCAL 欄位 expose 到 UI,不引入 FedRAMP catalog master data。

10.1 jedi-oscal 套件擴充(commit 8de7991

OscalSspSystemImplementationItem 加 2 個 nullable 欄位:

欄位 型別 用途
provider VARCHAR(255) 供應商名稱(service_name 之外的單獨欄位)
category VARCHAR(50) 服務類別(6 個 enum)

對應 entity / mapper 同步擴充。

10.2 BE 端到端擴充(commit 18a1110

  • migration SQL: scripts/sql/2026-05-23-add-leveraged-provider-category.sql(cmmgr 跑)
  • SspLeveragedContextService add/update/list/_to_dict 都 wire provider + category
  • 順手補之前漏接的 OSCAL status(之前 design §6 列了但沒實作)+ OSCAL purpose(同)
  • Excel 06 sheet SHEET_LEVERAGED 加 category column + 6 個 enum_values + i18n header

10.3 FE 表單擴充

SspLeveragedSection.vue 從原 design 的 4 欄位(名稱 / 日期 / 描述 + party_uuid)擴充到 7 欄位:

# 欄位 UI 對應 OSCAL
1 類別 Dropdown 6 enum category (新欄位)
2 服務名稱 InputText title
3 供應商 InputText provider (新欄位)
4 授權日期 Calendar date_authorized
5 服務狀態 Dropdown 3 enum status (新 expose)
6 用途 Textarea purpose (新 expose)
7 描述 Textarea description

party_uuid隱藏(user 不會知道 UUID 是什麼)— 保留 model + comment 預留未來 picker。

10.4 Category 6 個 enum 值

對齊 CMMC + FedRAMP common scenarios:

  • cloud_infrastructure(雲端基礎設施 - IaaS)
  • cloud_platform(雲端平台 - PaaS)
  • cloud_software(雲端軟體 - SaaS)
  • managed_security(資安託管服務 - MSSP)
  • identity_authentication(身分驗證 / IAM)
  • other(其他)

10.5 跟原 design §6 的差異

原 design §6 EntityFactory pattern 沿用,但 entity 構建時新欄位(provider / category / status / purpose)跟著一起 build;ssp_id 寫入時走 mapper hasattr guard(避免老 schema ORM model 沒新欄位時硬塞)。

11. Track B Reconciliation(2026-05-23 — Excel chain 補完)

Phase C C7 ship 後(leveraged 結構化欄位進 entity + SSP UI),Excel 端尚未跟上。Track B 補完整個 Excel chain(樣板 / parser / reconciler / write strategy / FE 預覽),詳見 2026-05-23-feat-ssp-excel-system-characteristic-and-leveraged-structured.md

11.1 Excel 樣板欄位 iteration

從 v2.1.0 → v2.4.0 共 3 次 schema bump:

版本 變更 觸發
v2.2.0 leveraged 加 status enum;同 phase 加 受評標的 sheet 初始 Track B
v2.3.0 sheet 名拿掉「01_」「02_」前綴;parser 加 legacy alias backward compat user 反饋雜亂
v2.3.1 SC + leveraged status enum 對齊 Billows convention (active vs operational) user 反饋預覽頁狀態無法帶出
v2.4.0 leveraged 加 date_authorized + description;provider 改 optional user 反饋對齊 SSP UI edit dialog 欄位

11.2 LeveragedWriteStrategy 修法(vs 原 Phase C 設計)

原 Phase C 設計:entity 加 provider 後直接從 parsed.provider 寫到 entity.provider

Track B 發現的 corner case:SSP UI display 讀的是 entity.title,而 LeveragedWriteStrategy 原本只寫 entity.name。Excel 匯入後 SSP UI 看到 bold 「—」+ small「服務名稱」(被誤當 provider 顯示)。

Fixservice_name 同時寫 entity.name + entity.title。BE 兩個欄位都填,UI 端讀 title 為主。

_dict_to_parsed_leveraged 拿掉 provider=provider or service_name cross-fill — user 沒填 provider 就維持空字串(之前 fallback 灌 service_name 到 provider 欄位)。

11.3 ParsedLeveraged 加 description

對齊 Excel v2.4.0 補的 description 欄位。_dict_to_parsed_leveraged 讀 dict.description; LeveragedWriteStrategy payload 帶 description;entity.description 寫入。

11.4 Person role validator 改 non-blocking

03 參與人員 sheet 的 role validator 原本強制 4 個 Billows participant role (manager / reviewer / auditor / viewer),但 MF 內 OSCAL responsible_parties 合法持有 OSCAL 標準角色 (system-owner / system-security-officer 等,OSCAL 文件結構需要)。Filled-mode 寫 OSCAL role 進 Excel,parser self-reject。

Fixvalidate_person_role blocking=True → False;code INVALID_ROLEROLE_NOT_BILLOWS_PARTICIPANT。Confirm-time 加 _LEGACY_BLOCKING_CODES_NOW_WARNING 過濾名單,舊 parse_job stored blocking=True INVALID_ROLE 也跟著放寬。

11.5 filled-mode 設備 / 資訊系統 / 外部利用服務 sheet 空白

User 反饋 Excel 下載 3 個 sheet 全空。Root cause:items 實際存 scope_type='ssp', scope_id=ssp_id(MF template-edit panel 走 ModuleFrameSspResourcesService 寫入時 force scope='ssp',C2.1 commit 設計), 但 _fetch_mf_itemsscope_type='module_frame' 查 → 永遠空。

Fix:新 _fetch_mf_items_via_ssp(mf) — resolve chain mirror Track B _build_system_characteristic:mf → profile → SSP → items by ssp_id。