# FR-048 Phase 2a — 授權守門盤點：api/module_frame

盤點日期：2026-07-07 ／ branch `fix/v1.8.0-bugs`
方法：對每個 WRITE method（post/put/delete/patch）與敏感 GET（export/download），追進 app-service body 找授權守門呼叫。

**總結**：`api/module_frame` 內所有 WRITE / 敏感 GET 端點皆**無任何角色 / participant / admin 授權守門**，只有 `@jwt_required()`（認證，非授權）。兩個 defaults service 的 docstring 明確註記「範本 SSP 沒 project participant，不能走 SspPermissionChecker」（`module_frame_control_default_service.py:7`、`module_frame_control_objective_default_service.py:7`），代表既有設計刻意未接 SSP 權限鏈，但也未替換成其他守門。`module_frame_service.py:263` 的 `is_admin=True` 是 `start_project_from_module_frame` 建立 participant 時的參數，非守門檢查。

## api/module_frame

| Route path | Method | Route file:line | @jwt_required? | 呼叫的 service method | 現況守門 | 備註 |
|---|---|---|---|---|---|---|
| `/api/1.0/module-frame`、`/api/1.0/module-frame/<uid>` | POST | module_frame_route.py:78 | 是 | `ModuleFrameService.add_module_frame` | 僅 JWT | 建立 module_frame |
| `/api/1.0/module-frame/<uid>` | PUT | module_frame_route.py:93 | 是 | `ModuleFrameService.update_module_frame` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>` | DELETE | module_frame_route.py:104 | 是 | `ModuleFrameService.delete_module_frame` | 僅 JWT | |
| （ModuleFramesRoute） | POST | module_frame_route.py:50 | 是 | `ModuleFrameService.get_module_frames_and_pager` | 僅 JWT | route 未註冊(disabled)；POST 實為分頁查詢(讀) |
| （ModuleFrameStartRoute） | POST | module_frame_route.py:115 | 是 | `ModuleFrameService.start_project_from_module_frame` | 僅 JWT | route 未註冊(disabled) |
| `/api/1.0/module-frame/clone` | POST | module_frame_route.py:133 | 是 | `ModuleFrameService.clone_module_frame` | 僅 JWT | |
| `/api/1.0/module-frame/item`、`/module-frame/item/<uid>` | POST | module_frame_item_route.py:42 | 是 | `ModuleFrameItemService.add_module_frame_item` | 僅 JWT | |
| `/api/1.0/module-frame/item/<uid>` | PUT | module_frame_item_route.py:57 | 是 | `ModuleFrameItemService.update_module_frame_item` | 僅 JWT | |
| `/api/1.0/module-frame/item/<uid>` | DELETE | module_frame_item_route.py:71 | 是 | `ModuleFrameItemService.delete_module_frame_item` | 僅 JWT | |
| `/api/1.0/module-frame/item/xml/<uid>` | PUT | module_frame_item_route.py:89 | 是 | `WorkflowTemplateService.update_workflow_template_xml` | 僅 JWT | service 屬 jedi_flow_engine，非 module_frame app 層 |
| `/api/1.0/module-frame/import/template` | POST | module_frame_import_route.py:55 | 是 | `ModuleFrameImportService.verify_import_module_frame_data_list` | 僅 JWT | 上傳 Excel + header 檢核（未落庫） |
| `/api/1.0/module-frame/import/verify` | POST | module_frame_import_route.py:95 | 是 | `ModuleFrameImportService.verify_import_module_frame_data_list` | 僅 JWT | 資料檢核（未落庫） |
| `/api/1.0/module-frame/import` | POST | module_frame_import_route.py:118 | 是 | `ModuleFrameImportService.save_import_module_frame_data` | 僅 JWT | 匯入落庫 |
| `/api/1.0/module-frame/import/yaml` | POST | module_frame_import_route.py:151 | 是 | `ModuleFrameImportService.import_yaml` | 僅 JWT | YAML 匯入落庫 |
| `/api/1.0/module-frame/download/template` | GET | module_frame_import_route.py:28 | 否 | 無（`send_from_directory` 靜態檔） | 無 JWT | 敏感 GET；下載靜態範例檔，無 `@jwt_required` |
| `/api/1.0/module-frame/download/yaml-template` | GET | module_frame_import_route.py:39 | 否 | 無（`send_from_directory` 靜態檔） | 無 JWT | 敏感 GET；下載靜態範例檔，無 `@jwt_required` |
| `/api/1.0/module-frame/<uid>/control-defaults/<control_identifier>` | PUT | module_frame_control_default_route.py:99 | 是 | `ModuleFrameControlDefaultService.upsert` | 僅 JWT | service docstring 註記刻意不走 SspPermissionChecker |
| `/api/1.0/module-frame/<uid>/control-defaults/<control_identifier>` | DELETE | module_frame_control_default_route.py:125 | 是 | `ModuleFrameControlDefaultService.delete` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/objective-defaults` | PUT | module_frame_control_objective_default_route.py:74 | 是 | `ModuleFrameControlObjectiveDefaultService.upsert` | 僅 JWT | 集合 URL upsert（identifiers 在 body） |
| `/api/1.0/module-frame/<uid>/objective-defaults/<objective_default_uid>` | DELETE | module_frame_control_objective_default_route.py:133 | 是 | `ModuleFrameControlObjectiveDefaultService.delete_by_uid` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/reference-documents` | POST | module_frame_reference_document_route.py:84 | 是 | `ModuleFrameReferenceDocumentService.upload_to_pool` | 僅 JWT | 登錄檔案到程序書池 |
| `/api/1.0/module-frame/<uid>/reference-documents/<doc_uid>` | PUT | module_frame_reference_document_route.py:116 | 是 | `ModuleFrameReferenceDocumentService.update_meta` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/reference-documents/<doc_uid>` | DELETE | module_frame_reference_document_route.py:144 | 是 | `ModuleFrameReferenceDocumentService.delete_from_pool` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/control-defaults/<control_identifier>/reference-documents` | POST | module_frame_reference_document_route.py:176 | 是 | `ModuleFrameReferenceDocumentService.attach_to_control_default` | 僅 JWT | attach mapping |
| `/api/1.0/module-frame/<uid>/control-defaults/<control_identifier>/reference-documents/<doc_uid>` | DELETE | module_frame_reference_document_route.py:201 | 是 | `ModuleFrameReferenceDocumentService.detach_from_control_default` | 僅 JWT | detach mapping |
| `/api/1.0/module-frame/<uid>/objective-defaults/<objective_default_uid>/reference-documents` | POST | module_frame_reference_document_route.py:236 | 是 | `ModuleFrameReferenceDocumentService.attach_to_objective_default` | 僅 JWT | attach mapping |
| `/api/1.0/module-frame/<uid>/objective-defaults/<objective_default_uid>/reference-documents/<doc_uid>` | DELETE | module_frame_reference_document_route.py:261 | 是 | `ModuleFrameReferenceDocumentService.detach_from_objective_default` | 僅 JWT | detach mapping |
| `/api/1.0/module-frame/<uid>/control-defaults/import/template` | POST | module_frame_template_import_route.py:134 | 是 | `ModuleFrameTemplateImportService.verify_template` | 僅 JWT | header 驗證（未落庫） |
| `/api/1.0/module-frame/<uid>/control-defaults/import/verify` | POST | module_frame_template_import_route.py:167 | 是 | `ModuleFrameTemplateImportService.verify_data` | 僅 JWT | 資料驗證（未落庫） |
| `/api/1.0/module-frame/<uid>/control-defaults/import/validate` | POST | module_frame_template_import_route.py:200 | 是 | `ModuleFrameTemplateImportService.validate_items` | 僅 JWT | 重驗（未落庫） |
| `/api/1.0/module-frame/<uid>/control-defaults/import` | POST | module_frame_template_import_route.py:233 | 是 | `ModuleFrameTemplateImportService.save_import` | 僅 JWT | 批次 upsert 落庫 |
| `/api/1.0/module-frame/<uid>/control-defaults/export` | GET | module_frame_template_import_route.py:67 | 是 | `ModuleFrameTemplateImportService.download_export` | 僅 JWT | 敏感 GET（Excel 匯出） |
| `/api/1.0/module-frame/<uid>/control-defaults/template/download` | GET | module_frame_template_import_route.py:95 | 是 | `ModuleFrameTemplateImportService.download_template` | 僅 JWT | 敏感 GET（Excel 範本下載，含 MF 控制項） |
| `/api/1.0/module-frame/<uid>/ssp-import-template` | GET | ssp_import_template_route.py:55 | 是 | `SspImportTemplateAppService.generate` | 僅 JWT | 敏感 GET（9-sheet SSP 樣板，filled 含 MF 資料） |
| `/api/1.0/ssp/<ssp_uid>/excel-template` | GET | ssp_import_template_route.py:97 | 是 | `SspImportTemplateAppService.generate_for_ssp` | 僅 JWT | 敏感 GET（filled 含該 SSP 實際資料） |
| `/api/1.0/ssp-import-template` | GET | ssp_import_template_route.py:138 | 是 | `SspImportTemplateAppService.generate_by_framework_version` | 僅 JWT | 敏感 GET（framework-version blank 樣板） |
| `/api/1.0/module-frame/<uid>/system-characteristic` | PUT | module_frame_system_characteristic_route.py:45 | 是 | `ModuleFrameSystemCharacteristicService.update` | 僅 JWT | FR-038 2B B3 re-enabled |
| `/api/1.0/module-frame/<uid>/components` | POST | module_frame_components_route.py:32 | 是 | `ModuleFrameComponentsService.add_item` | 僅 JWT | FR-038 2B B3 re-enabled |
| `/api/1.0/module-frame/<uid>/components/<item_uid>` | PUT | module_frame_components_route.py:49 | 是 | `ModuleFrameComponentsService.update_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/components/<item_uid>` | DELETE | module_frame_components_route.py:65 | 是 | `ModuleFrameComponentsService.delete_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/inventory` | POST | module_frame_inventory_route.py:32 | 是 | `ModuleFrameInventoryService.add_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/inventory/<item_uid>` | PUT | module_frame_inventory_route.py:49 | 是 | `ModuleFrameInventoryService.update_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/inventory/<item_uid>` | DELETE | module_frame_inventory_route.py:65 | 是 | `ModuleFrameInventoryService.delete_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/leveraged` | POST | module_frame_leveraged_route.py:43 | 是 | `ModuleFrameLeveragedService.add_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/leveraged/<item_uid>` | PUT | module_frame_leveraged_route.py:62 | 是 | `ModuleFrameLeveragedService.update_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/leveraged/<item_uid>` | DELETE | module_frame_leveraged_route.py:78 | 是 | `ModuleFrameLeveragedService.delete_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/parties` | POST | module_frame_party_route.py:42 | 是 | `ModuleFramePartyService.add_party` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/parties/<party_uid>` | PUT | module_frame_party_route.py:63 | 是 | `ModuleFramePartyService.update_party` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/parties/<party_uid>` | DELETE | module_frame_party_route.py:81 | 是 | `ModuleFramePartyService.remove_party` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/ssp-resources/items` | POST | module_frame_ssp_resources_route.py:51 | 是 | `ModuleFrameSspResourcesService.add_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/ssp-resources/items/<item_uid>` | PUT | module_frame_ssp_resources_route.py:70 | 是 | `ModuleFrameSspResourcesService.update_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/ssp-resources/items/<item_uid>` | DELETE | module_frame_ssp_resources_route.py:86 | 是 | `ModuleFrameSspResourcesService.delete_item` | 僅 JWT | |
| `/api/1.0/module-frame/<uid>/ssp-export` | GET | mf_ssp_export_route.py:51 | 是 | `SspExportAppService.export_from_module_frame` | 僅 JWT | 敏感 GET（SSP docx/pdf/odt 匯出）；無守門 |
