Files
CFDivePlatform/openspec/specs/role-authorization/spec.md
T
a620906209 d83d941993
Run Tests / test (pull_request) Successful in 34s
chore(openspec): 歸檔 add-role-middleware 並同步主規格
- 新增主規格 role-authorization,更新 feature-test-coverage 的 middleware 描述
- 勾選手動驗證任務 5.1、5.2(23/23)
- CLAUDE.md 授權規範與 openspec/config.yaml 移除「member/provider 只有 auth:sanctum」的過時說明

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 00:30:05 +08:00

69 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# role-authorization Specification
## Purpose
依使用者角色限制 member、provider 專屬 API 路由群組的存取,避免已登入的其他角色跨角色呼叫課程、時段、預約等業務端點。
## Requirements
### Requirement: Member 專屬路由僅限 member 角色存取
`/api/member/*` 路由群組(不含公開的 `POST /api/member/register`、`POST /api/member/login`)SHALL 只允許 `role = member` 的已登入使用者存取。其他角色 MUST 收到 HTTP 403,回應為 `{ "status": false, "message": "無權限存取" }`,且請求 MUST NOT 產生任何資料變更。未帶 token 的請求 MUST 收到 HTTP 401。
#### Scenario: member 可存取 member 路由
- **WHEN** 一個 `role = member` 的使用者,以該使用者的 token 呼叫 `GET /api/member/profile` 與 `GET /api/member/bookings`
- **THEN** 回傳 HTTP 200,`status` 為 `true`
#### Scenario: provider 呼叫 member 路由被拒絕
- **WHEN** 一個 `role = provider` 的使用者,以該使用者的 token 呼叫 `GET /api/member/profile`、`GET /api/member/bookings`、`POST /api/member/bookings`、`POST /api/member/reviews`
- **THEN** 每個請求都回傳 HTTP 403,body 為 `{ "status": false, "message": "無權限存取" }`
#### Scenario: admin 呼叫 member 路由被拒絕
- **WHEN** 一個 `role = admin` 的使用者,以該使用者的 token 呼叫 `GET /api/member/profile`、`GET /api/member/bookings`、`POST /api/member/bookings`
- **THEN** 每個請求都回傳 HTTP 403,body 為 `{ "status": false, "message": "無權限存取" }`
#### Scenario: 非 member 建立預約不會產生資料
- **WHEN** 一個可預約的課程時段,以及一個 `role = provider` 的使用者,以該使用者的 token 呼叫 `POST /api/member/bookings` 並帶入合法的時段 id
- **THEN** 回傳 HTTP 403,`bookings` 資料表筆數不變,該時段的 `current_participants` 不變
#### Scenario: 未登入呼叫 member 路由
- **WHEN** 不帶 token 呼叫 `GET /api/member/profile`、`GET /api/member/bookings`
- **THEN** 回傳 HTTP 401
### Requirement: Provider 專屬路由僅限 provider 角色存取
`/api/provider/*` 路由群組(不含公開的 `POST /api/provider/register`、`POST /api/provider/login`)SHALL 只允許 `role = provider` 的已登入使用者存取,且 MUST NOT 以教練審核狀態作為進入條件。其他角色 MUST 收到 HTTP 403,回應為 `{ "status": false, "message": "無權限存取" }`,且請求 MUST NOT 產生任何資料變更。未帶 token 的請求 MUST 收到 HTTP 401。
#### Scenario: provider 可存取 provider 路由
- **WHEN** 一個 `role = provider` 的使用者,以該使用者的 token 呼叫 `GET /api/provider/profile`、`GET /api/provider/offers`、`GET /api/provider/schedules`、`GET /api/provider/bookings`
- **THEN** 回傳 HTTP 200,`status` 為 `true`
#### Scenario: 未通過審核的 provider 仍可進入 provider 路由
- **WHEN** 一個 `role = provider`、`verification_status` 不是 approved(例如 unsubmitted 或 pending)的使用者,以該使用者的 token 呼叫 `GET /api/provider/verification`
- **THEN** 回傳 HTTP 200,不會因角色 middleware 回 403
#### Scenario: member 呼叫 provider 路由被拒絕
- **WHEN** 一個 `role = member` 的使用者,以該使用者的 token 呼叫 `GET /api/provider/profile`、`GET /api/provider/offers`、`POST /api/provider/offers`、`POST /api/provider/schedules`、`GET /api/provider/bookings`、`GET /api/provider/verification`
- **THEN** 每個請求都回傳 HTTP 403,body 為 `{ "status": false, "message": "無權限存取" }`
#### Scenario: admin 呼叫 provider 路由被拒絕
- **WHEN** 一個 `role = admin` 的使用者,以該使用者的 token 呼叫 `GET /api/provider/profile`、`GET /api/provider/offers`、`POST /api/provider/offers`、`GET /api/provider/bookings`
- **THEN** 每個請求都回傳 HTTP 403,body 為 `{ "status": false, "message": "無權限存取" }`
#### Scenario: 非 provider 建立課程不會產生資料
- **WHEN** 一個 `role = member` 的使用者,以該使用者的 token 呼叫 `POST /api/provider/offers` 並帶入通過驗證的完整課程資料
- **THEN** 回傳 HTTP 403,`diving_offers` 資料表筆數不變
#### Scenario: 未登入呼叫 provider 路由
- **WHEN** 不帶 token 呼叫 `GET /api/provider/profile`、`GET /api/provider/offers`
- **THEN** 回傳 HTTP 401
### Requirement: 跨角色共用路由不受角色限制影響
通知(`/api/notifications/*`)與預約即時訊息(`/api/bookings/{booking}/messages*`、`/api/bookings/messages/unread-counts`)路由 SHALL 維持既有行為:member 與 provider 皆可存取,存取權限由既有的資源歸屬檢查決定,不套用 member 或 provider 角色限制。
#### Scenario: member 與 provider 皆可讀取通知
- **WHEN** 一個 `role = member` 與一個 `role = provider` 的使用者,分別以各自的 token 呼叫 `GET /api/notifications`
- **THEN** 兩者皆回傳 HTTP 200
#### Scenario: member 與 provider 皆可讀取未讀訊息數
- **WHEN** 一個 `role = member` 與一個 `role = provider` 的使用者,分別以各自的 token 呼叫 `GET /api/bookings/messages/unread-counts`
- **THEN** 兩者皆回傳 HTTP 200