Yuruicamp

G-1/G-5 Admin RBAC Swagger 驗證

驗證目的

這份流程驗證以下行為:

驗證前準備

  1. docs/seed/README.md 載入最新開發 Seed。
  2. 確認 PostgreSQL 已啟動,並啟動 Spring Boot Backend。
  3. 開啟 http://localhost:8080/swagger-ui.html
  4. 確認 yuruicamp.firebase.enabled=false,本機才能使用 dev: Token。
  5. 本流程使用 Seed 內的管理員:
email: booking-seed@example.test
role: admin
active: true

首次驗證建議使用固定 Token,避免同一個 email 被不同 UID 重複綁定:

dev:booking-seed-admin:booking-seed@example.test:google:Booking Seed Admin

如果此資料庫曾用其他 UID 登入該 email,應重新載入乾淨 Seed,或改用原本已綁定的 UID。已綁定帳號使用不同 UID 時,後端會拒絕登入。

第一階段:建立 Admin Session

1. 呼叫 Admin Session

Admin Auth 執行:

POST /api/admin/auth/firebase/session

Request Body:

{
  "idToken": "dev:booking-seed-admin:booking-seed@example.test:google:Booking Seed Admin"
}

預期結果:

這個端點只建立/確認 Admin Session,不會簽發後端 JWT。後續 API 仍使用同一個 Firebase/dev: Token。

2. 設定 Swagger Authorize

點選 Swagger 右上角 Authorize,輸入:

dev:booking-seed-admin:booking-seed@example.test:google:Booking Seed Admin

若 Swagger 欄位說明要求完整 Header,再輸入:

Bearer dev:booking-seed-admin:booking-seed@example.test:google:Booking Seed Admin

專案的 Bearer Security Scheme 通常只需 Token 本體,Swagger 會自動補上 Bearer

第二階段:驗證權限字典

執行:

GET /api/admin/permissions

預期結果:

預期的完整 permission code:

analytics.view
analytics.edit
orders.view
orders.edit
movement.view
movement.edit
products.view
products.edit
customers.view
customers.edit
discounts.view
discounts.edit
reviews.view
reviews.edit
booking-calendar.view
booking-calendar.edit
bookings.view
bookings.edit
permissions.view
permissions.edit

如果回傳不是 20 筆,先確認 docs/seed/dev/010-reference.sql 是否已載入,不要直接繼續權限覆寫測試。

第三階段:建立 Operator 白名單

執行:

POST /api/admin/users

Request Body:

{
  "name": "Swagger RBAC Operator",
  "email": "swagger-rbac-operator@example.test",
  "role": "operator"
}

預期結果:

記下回傳的 data.id。再次用相同 email 建立時,預期回傳 HTTP 409 CONFLICT,不能建立重複白名單。

第四階段:驗證列表與詳情

1. 分頁列表

執行:

GET /api/admin/users?page=0&size=20

預期結果:

再以 page=-1size=101 呼叫,預期 HTTP 400 VALIDATION_ERROR

2. 管理員詳情

將路徑中的 {id} 替換成 OPERATOR_ID

GET /api/admin/users/{id}

預期結果:

不存在的 ID 預期回傳 HTTP 404 NOT_FOUND

第五階段:設定只讀權限覆寫

PUT /api/admin/users/{id}/permissions 使用「完整集合取代」語意,Request 必須包含全部 20 個 code,不能只傳要修改的兩個欄位。

執行:

PUT /api/admin/users/{OPERATOR_ID}/permissions

Request Body:

{
  "permissions": {
    "analytics.view": true,
    "analytics.edit": false,
    "orders.view": true,
    "orders.edit": true,
    "movement.view": false,
    "movement.edit": false,
    "products.view": false,
    "products.edit": false,
    "customers.view": true,
    "customers.edit": false,
    "discounts.view": true,
    "discounts.edit": false,
    "reviews.view": true,
    "reviews.edit": true,
    "booking-calendar.view": true,
    "booking-calendar.edit": false,
    "bookings.view": true,
    "bookings.edit": true,
    "permissions.view": true,
    "permissions.edit": false
  }
}

預期結果:

額外驗證:

第六階段:使用 Operator Token 驗證 RBAC

1. 第一次登入並綁定 UID

先在 Swagger 的 Authorize 清除原本 Admin Token。

執行:

POST /api/admin/auth/firebase/session

Request Body:

{
  "idToken": "dev:swagger-rbac-operator:swagger-rbac-operator@example.test:google:Swagger RBAC Operator"
}

預期 HTTP 200,並確認:

接著在 Swagger Authorize 改用:

dev:swagger-rbac-operator:swagger-rbac-operator@example.test:google:Swagger RBAC Operator

2. 驗證只讀成功

使用 Operator Token 呼叫:

GET /api/admin/users?page=0&size=20
GET /api/admin/permissions
GET /api/admin/users/{OPERATOR_ID}

三個端點都應回 HTTP 200,證明 permissions.view 已在每次請求重新解析並生效。

3. 驗證寫入被阻擋

仍使用 Operator Token 呼叫:

POST /api/admin/users
PATCH /api/admin/users/{OPERATOR_ID}
PUT /api/admin/users/{OPERATOR_ID}/permissions

預期三者都回 HTTP 403 FORBIDDEN。這一步是 RBAC 的核心驗證:即使前端顯示或誤觸寫入按鈕,後端仍必須阻擋沒有 permissions.edit 的帳號。

第七階段:驗證帳號停用即時生效

1. 改回 Admin Token

在 Swagger Authorize 清除 Operator Token,改回 Seed Admin Token:

dev:booking-seed-admin:booking-seed@example.test:google:Booking Seed Admin

2. 停用 Operator

執行:

PATCH /api/admin/users/{OPERATOR_ID}

Request Body:

{
  "active": false
}

預期 HTTP 200,且 data.active=false

3. 使用舊 Operator Token 重試

再次將 Swagger Authorize 換成 Operator Token,呼叫:

GET /api/admin/users?page=0&size=20

預期回 HTTP 401403,錯誤代碼依 Filter 進入點可能為 UNAUTHORIZEDFORBIDDENADMIN_INACTIVE。重點是舊 Token 不得繼續存取 Admin API,證明後端每次請求都會重查帳號啟用狀態。

第八階段:驗證管理員保護規則

以下操作必須改回 Seed Admin Token。

1. 不可停用自己

先由 GET /api/admin/users 找到 booking-seed@example.test 的 ID,以下以 SEED_ADMIN_ID 表示。

執行:

PATCH /api/admin/users/{SEED_ADMIN_ID}

Request Body:

{
  "active": false
}

預期 HTTP 409 CONFLICT,訊息指出不可停用自己的管理員帳號。

2. 不可移除自己的 permissions.edit

呼叫:

PUT /api/admin/users/{SEED_ADMIN_ID}/permissions

Request 必須仍傳完整 20 個 code,但將:

"permissions.view": true,
"permissions.edit": false

預期 HTTP 409 CONFLICT,且原本的 permissions.edit 仍然有效。

3. 不可移除最後一位啟用中的 admin

只有一位啟用中 admin 時,對該帳號執行下列任一操作:

{
  "role": "operator"
}

或:

{
  "active": false
}

預期 HTTP 409 CONFLICT。如果資料庫已有其他啟用中的 admin,這個情境不成立;應使用乾淨 Seed,或先確認目前啟用中的 admin 數量。

驗收完成標準

Swagger 驗證可確認 Token、Request JSON、Response Envelope 與端點層 @PreAuthorize 能人工串通。角色覆寫合併、悲觀鎖與兩個請求同時修改最後管理員的競爭情境,仍應由 AdminRbacPostgreSqlIntegrationTest 驗證,因為 Swagger 無法可靠製造並行交易。