FR-048 Phase 2a — 授權守門盤點:survey-group

事實盤點(no code change)。分支 fix/v1.8.0-bugs。 涵蓋模組:api/surveyapi/task_survey。 路徑前綴 /api/1.0(兩個 blueprint 都是)。 每個 WRITE method(post/put/delete/patch)一列;敏感 GET(export/download/report)一列。

總結論:兩模組全站無任何角色 / participant / admin 守門。所有 service(本地 app/surveyapp/task_survey + 外部 jedi_survey 套件)trace 到底皆無 assert_project_manager / require_platform_admin / require_super_admin / SspPermissionChecker / assert_workflow_project_participant / 手動 ForbiddenError。task_survey_service 內唯一的 TaskAssignee 引用(task_survey_service.py:461)是「通知收件人查詢」,非授權。已驗證 survey 為裸區屬實。 另有 2 個敏感 GET(範本下載)連 @jwt_required() 都沒有 → 完全未認證。

§1

api/survey

Route path Method Route file:line @jwt_required? 呼叫的 service method 現況守門 備註
/api/1.0/surveys POST api/survey/routes/survey_route.py:59 SurveyAppService.get_surveys_and_pager 僅 JWT POST 實為分頁查詢(讀)
/api/1.0/survey / /survey/<uid> POST api/survey/routes/survey_route.py:103 SurveyService.add_survey(jedi_survey) 僅 JWT 新增問卷;裸區
/api/1.0/survey/<uid> PUT api/survey/routes/survey_route.py:127 SurveyService.update_survey(jedi_survey) 僅 JWT 更新問卷;裸區
/api/1.0/survey/<uid> DELETE api/survey/routes/survey_route.py:146 SurveyService.delete_survey(jedi_survey) 僅 JWT 刪除問卷;裸區
/api/1.0/survey/excel-import POST api/survey/routes/survey_route.py:157 SurveyService.import_survey(jedi_survey) 僅 JWT Excel 匯入問卷(寫入);裸區
/api/1.0/survey/import-template GET api/survey/routes/survey_route.py:198 無(send_from_directory 靜態檔) 無 JWT 敏感 GET(範本下載);連 JWT 都缺
/api/1.0/survey/move POST api/survey/routes/survey_route.py:209 SurveyService.move_survey(jedi_survey) 僅 JWT 移動問卷(寫入);裸區
/api/1.0/survey/<uid>/clone POST api/survey/routes/survey_route.py:229 SurveyCloneService.clone_survey 僅 JWT 深度複製問卷(寫入);裸區
/api/1.0/survey/<uid>/release PUT api/survey/routes/survey_route.py:252 SurveyDomainService.release_survey(jedi_survey) 僅 JWT 發布問卷(publish);裸區
/api/1.0/survey/<uid>/release DELETE api/survey/routes/survey_route.py:266 SurveyDomainService.unrelease_survey(jedi_survey) 僅 JWT 取消發布(unpublish);裸區
/api/1.0/survey-folders POST api/survey/routes/survey_folder_route.py:39 SurveyFolderService.get_folders(jedi_survey) 僅 JWT POST 實為分頁查詢(讀)
/api/1.0/survey-folder / /survey-folder/<uid> POST api/survey/routes/survey_folder_route.py:67 SurveyFolderService.add_folder(jedi_survey) 僅 JWT 新增資料夾;裸區
/api/1.0/survey-folder/<uid> PUT api/survey/routes/survey_folder_route.py:82 SurveyFolderService.update_folder(jedi_survey) 僅 JWT 更新資料夾;裸區
/api/1.0/survey-folder/<uid> DELETE api/survey/routes/survey_folder_route.py:95 SurveyFolderService.delete_folder(jedi_survey) 僅 JWT 刪除資料夾;裸區
/api/1.0/survey-question / /survey-question/<uid> POST api/survey/routes/survey_question_route.py:34 SurveyQuestionDomainService.add_question(jedi_survey) 僅 JWT 新增題目;裸區
/api/1.0/survey-question/<uid> PUT api/survey/routes/survey_question_route.py:61 SurveyQuestionDomainService.update_question(jedi_survey) 僅 JWT 更新題目;裸區
/api/1.0/survey-question/<uid> DELETE api/survey/routes/survey_question_route.py:91 SurveyQuestionDomainService.delete_question(jedi_survey) 僅 JWT 刪除題目;裸區
/api/1.0/survey-questions/reorder POST api/survey/routes/survey_question_route.py:106 SurveyQuestionDomainService.reorder_questions(jedi_survey) 僅 JWT 題目重排序(寫入);裸區
/api/1.0/survey-question/move POST api/survey/routes/survey_question_route.py:125 SurveyQuestionDomainService.move_question(jedi_survey) 僅 JWT 移動題目(寫入);裸區
/api/1.0/survey-page / /survey-page/<uid> POST api/survey/routes/survey_page_route.py:34 SurveyPageDomainService.add_page(jedi_survey) 僅 JWT 新增頁面;裸區
/api/1.0/survey-page/<uid> PUT api/survey/routes/survey_page_route.py:60 SurveyPageDomainService.update_page(jedi_survey) 僅 JWT 更新頁面;裸區
/api/1.0/survey-page/<uid> DELETE api/survey/routes/survey_page_route.py:83 SurveyPageDomainService.delete_page(jedi_survey) 僅 JWT 刪除頁面;裸區
/api/1.0/survey-pages/reorder POST api/survey/routes/survey_page_route.py:97 SurveyPageDomainService.reorder_pages(jedi_survey) 僅 JWT 頁面重排序(寫入);裸區
/api/1.0/survey-page/move POST api/survey/routes/survey_page_route.py:116 SurveyPageDomainService.move_page(jedi_survey) 僅 JWT 移動頁面(寫入);裸區
/api/1.0/survey-discussions POST api/survey/routes/survey_discussion_route.py:26 SurveyDiscussionService.get_survey_discussions 僅 JWT POST 實為查詢(讀)
/api/1.0/survey-discussion / /survey-discussion/<uid> POST api/survey/routes/survey_discussion_route.py:44 SurveyDiscussionService.add_discussion 僅 JWT 新增討論;裸區
/api/1.0/survey-discussion/<uid> PUT api/survey/routes/survey_discussion_route.py:64 SurveyDiscussionService.update_discussion 僅 JWT 更新討論;裸區
/api/1.0/survey-discussion/<uid> DELETE api/survey/routes/survey_discussion_route.py:83 SurveyDiscussionService.delete_discussion 僅 JWT 刪除討論;裸區
§2

api/task_survey

Route path Method Route file:line @jwt_required? 呼叫的 service method 現況守門 備註
/api/1.0/task-surveys POST api/task_survey/routes/task_survey_route.py:31 TaskSurveyService.get_task_surveys 僅 JWT POST 實為查詢(讀)
/api/1.0/task-survey/<uid> PUT api/task_survey/routes/task_survey_route.py:50 TaskSurveyService.update_task_survey 僅 JWT 更新 task survey(可換問卷);有 project/round context 但檢查 participant
/api/1.0/task-survey/import-template/<task_uid> GET api/task_survey/routes/task_survey_route.py:62 TaskSurveyService.generate_task_survey_question_import_excel_template_data 無 JWT 敏感 GET(範本下載 xlsx,含 task 題目內容);連 JWT 都缺
/api/1.0/task-survey/import-answers POST api/task_survey/routes/task_survey_route.py:130 TaskSurveyService.batch_import_task_survey_questions 僅 JWT Excel 批次上傳專案調查結果(寫入);未檢查 participant
/api/1.0/task-survey/configured/<uid> PUT api/task_survey/routes/task_survey_route.py:169 TaskSurveyService.update_task_survey_configure 僅 JWT 更新 configured(approval_required/換卷,寫入);未檢查 participant
/api/1.0/project-survey/answers/<uid> PUT api/task_survey/routes/question_answer_route.py:38 QuestionAnswerService.update_task_survey_answer 僅 JWT 更新答案(寫入);填答者面(assignee) 但未驗 assignee/participant
/api/1.0/project-survey/answers/<uid>/patch POST api/task_survey/routes/question_answer_route.py:55 QuestionAnswerService.patch_task_survey_answer 僅 JWT 單題批次 patch(socket/keepalive flush 寫入);填答者面(assignee) 但未驗
/api/1.0/project-survey/answers/<uid>/checkpoint POST api/task_survey/routes/question_answer_route.py:85 QuestionAnswerService.checkpoint_task_survey_answer 僅 JWT 暫存/提交答案(寫入);填答者面(assignee) 但未驗
/api/1.0/project-survey/histories POST api/task_survey/routes/question_answer_history_route.py:35 QuestionAnswerHistoryService.get_all_question_answer_history 僅 JWT POST 實為查詢(讀)
/api/1.0/project-survey/history/revert POST api/task_survey/routes/question_answer_history_route.py:46 QuestionAnswerHistoryService.revert_question_answer_from_history 僅 JWT 還原歷史答案(寫入,會 socket emit reload);未驗 participant
/api/1.0/project-survey/history-details POST api/task_survey/routes/question_answer_history_detail_route.py:21 QuestionAnswerHistoryDetailService.get_all_question_answer_history_detail 僅 JWT POST 實為查詢(讀)

註記

  • 敏感 GET(未認證,最高優先)
    • GET /api/1.0/survey/import-template(survey_route.py:198)— 純靜態檔下載,無 JWT。
    • GET /api/1.0/task-survey/import-template/<task_uid>(task_survey_route.py:62)— 動態產生含該 task 實際題目/答案選項的 xlsx,無 JWT,任意人帶 task_uid 可下載題庫內容。
  • task_survey 有 project/round context 卻不檢查update_task_survey / update_task_survey_configure / answer 系列 / revert 皆能經 task_survey → task_id 反查所屬專案輪次,但無一處驗證呼叫者是否為該專案 participant 或 assignee。
  • 未列入表格的純讀 GET(SurveyMenuRoute GET、SurveyRoute GET、SurveyFolderMenuRoute/SurveyFolderRoute GET、SurveysRoute 已列、TaskSurveyRoute GET、TaskSurveysAnswersRoute GET、QuestionAnswerHistoryMenuRoute GET)皆 @jwt_required() 且非 export/download/report,故不納敏感 GET 清單。