merge: rebase 到最新 main(吸納 PR #15 個人自助指令群)後新增管理端指令群
- main 已合併 PR #15(profile/password/email/sessions/tokens/mfa 與 eqrcode 依賴) - 保留 main 的 self_service 模組與 api.ex 個人自助端點,於其上重套管理端: jwks/accounts/audit-logs 指令、API 端點、CLI 接線、docs §3.8–§3.10、README 表格 - mix precommit 全綠(150 tests:admin 100 + self-service 50)
This commit is contained in:
+21
-34
@@ -338,9 +338,9 @@ bear apps update <client-id> [--url URL] [--title TITLE]
|
||||
|
||||
---
|
||||
|
||||
### 3.7 `bear profile`:個人自助指令群(P3,規劃中)
|
||||
### 3.7 `bear profile`:個人自助指令群(P3,已實作)
|
||||
|
||||
> 依賴:bear 伺服器端**個人自助 JSON API**(alterminal/bear#35,資源前綴 `/api/v1/profile`,PAT Bearer 認證、**任何有效 PAT 帳號**可用,不限 admin)。API 落地前本節僅為規格;端點細節以 #35 定案為準。
|
||||
> 依賴:bear 伺服器端**個人自助 JSON API**(alterminal/bear#35,資源前綴 `/api/v1/profile`,PAT Bearer 認證、**任何有效 PAT 帳號**可用,不限 admin)。API 已上線(PR alterminal/bear#38),本節指令群已實作(issue #11);端點細節以 #38 實作為準。
|
||||
|
||||
比照 `bear apps` 的「資源群組」例外:個人自助操作若攤平命名同樣會造成指令名爆炸(`bear password-change`…),收斂為 `bear <資源> <動詞>` 的六個子群組。頭像(picture)檔案上傳需要 multipart 與本機圖片,終端體驗差,**不在本期範圍**(#35 有對應端點,未來有需要再評估);如需更換頭像,可自行上傳圖片後以 `profile set --picture URL` 設定。
|
||||
|
||||
@@ -421,7 +421,7 @@ bear profile set [--name NAME] [--given-name NAME] [--family-name NAME]
|
||||
|
||||
1. 互動提示(不回顯)依序讀取:`目前密碼`、`新密碼`、`確認新密碼`。
|
||||
2. 兩次新密碼不一致 → 退出碼 `2`(本地檢查),不發請求。
|
||||
3. `POST /api/v1/profile/password`(Bearer PAT,body 帶 `current_password`/`new_password`)。
|
||||
3. `POST /api/v1/profile/password`(Bearer PAT,body 帶 `current_password`/`new_password`/`password_confirmation`;CLI 以兩次輸入一致的新密碼填 `password_confirmation`)。
|
||||
4. 成功 → 印出 `密碼已變更`;現行密碼錯誤 → 退出碼 `1`;新密碼不符伺服器強度規則 → `422`,退出碼 `1`。
|
||||
|
||||
比照網頁流程,變更密碼**不**自動撤銷其他 session;需要時另用 `bear sessions revoke-others`。
|
||||
@@ -437,8 +437,8 @@ bear profile set [--name NAME] [--given-name NAME] [--family-name NAME]
|
||||
**行為**:
|
||||
|
||||
1. `POST /api/v1/profile/email/request-codes`(Bearer PAT,`new_email`)→ 伺服器向**現有與新**信箱各寄一組驗證碼。
|
||||
2. 終端印出 `驗證碼已寄至 alice@example.com 與 new@example.com`,互動提示(不回顯)讀取:`現有信箱驗證碼`、`新信箱驗證碼`。
|
||||
3. `POST /api/v1/profile/email`(`current_code`/`new_code`)。
|
||||
2. 終端印出 `驗證碼已寄至現有信箱與 <new>`,互動提示(不回顧)讀取:`現有信箱驗證碼`、`新信箱驗證碼`。
|
||||
3. `POST /api/v1/profile/email`(`new_email`/`current_code`/`new_code`;API 版需再帶一次 `new_email`,CLI 自動帶入)。
|
||||
4. 成功 → 印出 `email 已更新:new@example.com`,並提醒 `bear login` 用的 PAT 不受影響(PAT 不綁 email)。
|
||||
|
||||
錯誤:`same_email`/`email_taken`/`invalid_email` → 退出碼 `1`;驗證碼錯誤或逾期 → 退出碼 `1`,提示重新執行(會重寄驗證碼);伺服器限流(`429`)→ 退出碼 `1`,提示稍後再試。
|
||||
@@ -452,11 +452,11 @@ bear profile set [--name NAME] [--given-name NAME] [--family-name NAME]
|
||||
- **`bear sessions list`**:`GET /api/v1/profile/sessions`(Bearer PAT),表格輸出,新到舊排序:
|
||||
|
||||
```
|
||||
ID IP USER AGENT CREATED LAST SEEN EXPIRES
|
||||
0192a1b0-… 203.0.113.10 Mozilla/5.0 (X11; Linux…) 2026-09-07 10:00:00Z 2026-09-08 09:00:00Z 2026-09-21 10:00:00Z
|
||||
ID IP USER AGENT CREATED LAST SEEN
|
||||
0192a1b0-… 203.0.113.10 Mozilla/5.0 (X11; Linux…) 2026-09-07 10:00:00Z 2026-09-08 09:00:00Z
|
||||
```
|
||||
|
||||
(`user_agent` 截斷至欄寬;`--json` 輸出完整值。)
|
||||
(`user_agent` 截斷至欄寬;`--json` 輸出完整值。API 回應欄位為 `id`/`ip`/`user_agent`/`created_at`/`last_seen_at`,不含 `expires_at`。)
|
||||
- **`bear sessions revoke <id>`**:`POST /api/v1/profile/sessions/{id}/revoke`。成功印出 `session 已撤銷`;不存在或非本人 → `404`,退出碼 `1`。
|
||||
- **`bear sessions revoke-others`**:`POST /api/v1/profile/sessions/revoke-others`。成功印出 `已撤銷 N 個其他 session`。
|
||||
|
||||
@@ -473,11 +473,11 @@ ID IP USER AGENT
|
||||
```
|
||||
ID NAME SCOPE CREATED EXPIRES LAST USED STATUS
|
||||
0192a1c0-… ci-token openid, profile 2026-09-01 00:00:00Z 2026-10-01 00:00:00Z 2026-09-08 08:00:00Z active
|
||||
0192a1b0-… old-laptop openid, profile 2026-08-01 00:00:00Z (無到期) (未使用) revoked
|
||||
0192a1b0-… old-laptop openid, profile 2026-08-01 00:00:00Z (無到期) (未使用) expired
|
||||
```
|
||||
|
||||
`STATUS`:`active`/`expired`/`revoked`(CLI 依 `expires_at`/`revoked_at` 本地判定)。
|
||||
- **`bear tokens create --name NAME [--expires-in DAYS|never] [--scope SCOPES]`**:`POST /api/v1/profile/tokens`(Bearer PAT)。成功後一次性顯示:
|
||||
`STATUS`:`active`/`expired`(CLI 依 `expires_at` 本地判定)。API 的 list 端點只回**未撤銷**的 token,故 `revoked` 不會出現在清單。
|
||||
- **`bear tokens create --name NAME [--expires-in DAYS|never]`**:`POST /api/v1/profile/tokens`(Bearer PAT,body `name`/`expires_in`;scope 由伺服器給預設 `openid profile email`,API 不接受指定)。成功後一次性顯示:
|
||||
|
||||
```
|
||||
PAT 已建立:ci-token(30 天後到期)
|
||||
@@ -489,20 +489,19 @@ token(只顯示這一次,請立即保存):
|
||||
|------|------|------|
|
||||
| `--name` | (必填) | 顯示名稱;空白或重複名稱依伺服器規則 `422` → 退出碼 `1` |
|
||||
| `--expires-in` | `30` | 天數(正整數)或 `never`(永不過期) |
|
||||
| `--scope` | `openid profile email` | 空白分隔的 scope 清單(同伺服器預設) |
|
||||
|
||||
`--json` 輸出範例:`{"ok": true, "token": {…list 之單一物件…}, "raw": "9f3a…"}`(`raw` 只出現這一次。)
|
||||
提醒:新 PAT 要使用須另以 `bear login --token` 登入(或設 `BEAR_TOKEN`)。
|
||||
- **`bear tokens revoke <id>`**:`POST /api/v1/profile/tokens/{id}/revoke`(`<id>` 為 UUID)。成功印出 `PAT 已撤銷:<name>`;若撤銷的是**目前憑證用的 PAT**,後續指令會得到 `401`(退出碼 `3`),屆時重新 `bear login`;CLI 在撤銷成功後偵測到 `id` 對應本機憑證時,主動提示執行 `bear logout` 清除本機憑證。
|
||||
- **`bear tokens revoke <id>`**:`POST /api/v1/profile/tokens/{id}/revoke`(`<id>` 為 UUID)。成功印出 `PAT 已撤銷`;若撤銷的是**目前憑證用的 PAT**,後續指令會得到 `401`(退出碼 `3`),屆時重新 `bear login`(本機憑證檔未存 token id,CLI 無法預先比對,僅能提示此退路)。
|
||||
|
||||
#### 3.7.7 `bear mfa`
|
||||
|
||||
TOTP 兩因子管理(WebAuthn/passkey 不在範圍)。
|
||||
|
||||
- **`bear mfa status`**:讀 `GET /api/v1/profile` 的 MFA 欄位,印出 `MFA(TOTP):已啟用/未啟用`。
|
||||
- **`bear mfa status`**:讀 `GET /api/v1/profile` 的 `mfa_enabled` 欄位,印出 `MFA(TOTP):已啟用/未啟用`。
|
||||
- **`bear mfa setup`**:兩步互動流程——
|
||||
1. `POST /api/v1/profile/mfa/setup`(無 code,Bearer PAT)→ 回傳 Base32 `secret` 與 `otpauth://` URI。終端印出 URI、secret,並在本機產生 ASCII QR(掃碼加入驗證器;QR 由 CLI 本地生成,不經伺服器)。
|
||||
2. 互動提示(不回顯)讀取 6 位碼 → `POST /api/v1/profile/mfa/setup`(帶 code)確認。
|
||||
1. `POST /api/v1/profile/mfa/setup`(無 body,Bearer PAT)→ 回傳 Base32 `secret` 與 `otpauth://` URI。終端印出 URI、secret,並在本機產生 ASCII QR(掃碼加入驗證器;QR 由 CLI 本地生成,不經伺服器;`--json` 模式此步資訊走 stderr,stdout 留給最終結果)。
|
||||
2. 互動提示(不回顧)讀取 6 位碼 → `POST /api/v1/profile/mfa/setup/confirm`(`code`)確認。
|
||||
3. 成功 → **一次性**顯示 recovery codes(提示同 PAT:只顯示這一次)。
|
||||
|
||||
已啟用 MFA 再執行 → 退出碼 `1`,提示已啟用。驗證碼錯誤 → 退出碼 `1`,可重試(不重產 secret)。
|
||||
@@ -681,7 +680,7 @@ TIME CATEGORY EVENT ACTOR ACCOUN
|
||||
| `5` | 授權被拒絕(access_denied) |
|
||||
| `6` | 網路/伺服器錯誤 |
|
||||
| `7` | 設定檔/憑證檔格式損毀 |
|
||||
| `8` | 權限不足(HTTP 403;管理端操作僅限 admin:apps/jwks/accounts/audit-logs) |
|
||||
| `8` | 權限不足(HTTP 403;管理端操作僅限 admin:apps/jwks/accounts/audit-logs) |
|
||||
|
||||
---
|
||||
|
||||
@@ -705,7 +704,6 @@ TIME CATEGORY EVENT ACTOR ACCOUN
|
||||
- 所有診斷訊息走 stderr;`--json` 模式將錯誤以 `{"ok": false, "error": "...", "code": N}` 輸出。
|
||||
- token、user_code、device_code 一律不入 log、不入 stdout(除 `bear token` 之目的外)、不入版本庫。
|
||||
- `client_secret`(App 管理)僅於 `apps create`/`apps rotate-secret` 成功當下一次性輸出;`403` 顯示「此操作需 admin 權限」並以退出碼 `8` 結束,不重試。
|
||||
- 管理端指令群(jwks/accounts/audit-logs)同樣 admin 限定;帳號的初始/新密碼僅於 `accounts create`/`accounts set-password` 成功當下一次性輸出;密碼不進命令列(互動輸入不回顧,或 `--generate-password`);JWK 的 `key_data` 永不經顯示。
|
||||
- 個人自助的敏感輸入(密碼、email/MFA 驗證碼)一律互動提示、不回顧、不接受命令列參數;一次性明文(`tokens create` 的 PAT、`mfa` 的 recovery codes)僅於成功當下輸出(見 §3.7)。
|
||||
- 網路層一律使用 `Req`,遵循 bear 倉庫 HTTP 客戶端慣例。
|
||||
|
||||
@@ -719,8 +717,8 @@ TIME CATEGORY EVENT ACTOR ACCOUN
|
||||
4. 是否支援 `offline_access` scope,或沿用現行 refresh 輪轉即可?
|
||||
5. 是否在 CLI 提供「建立/撤銷 PAT」?原規劃傾向 CLI 只「使用」不「管理」;#35 的 PAT 端點落地後,`bear tokens` 已納入 §3.7.6 規格(管理自己帳號的 PAT),此題視為已解。
|
||||
6. App 管理 API 的路徑參數最終採 UUID 或 `client_id`(alterminal/bear#28 規格為 `{id}`)?若為 UUID,CLI 需先以 list 解析 `client_id` → UUID,或伺服器提供以 `client_id` 直取的端點。
|
||||
7. `bear profile set --phone-verified`(自我標記電話已驗證)是否開放?#35 需決定 API 是否接受該欄位;不開放則 CLI 移除此選項。
|
||||
8. `mfa setup` 的 `secret`/`otpauth://` 屬敏感資料,#35 的 API 回應是否應限制僅 setup 流程期間回傳(未確認前不重送)?
|
||||
7. `bear profile set --phone-verified`(自我標記電話已驗證)是否開放?#35 已定案:API 接受該欄位(`profile_changeset` 的 `profile_fields` 含 `phone_number_verified`),CLI 保留 `--phone-verified` 旗標(僅設定,清除不在本期)。
|
||||
8. `mfa setup` 的 `secret`/`otpauth://` 屬敏感資料,#35 的 API 回應是否應限制僅 setup 流程期間回傳(未確認前不重送)?#38 實作為 setup 回應一次性回傳,未確認前重呼 setup 會重產 secret;CLI 依此實作(第二步走 `/mfa/setup/confirm`)。
|
||||
|
||||
---
|
||||
|
||||
@@ -750,20 +748,9 @@ TIME CATEGORY EVENT ACTOR ACCOUN
|
||||
| `tokens create` | `POST /api/v1/profile/tokens` |
|
||||
| `tokens revoke <id>` | `POST /api/v1/profile/tokens/{id}/revoke` |
|
||||
| `mfa status` | `GET /api/v1/profile`(讀 MFA 欄位) |
|
||||
| `mfa setup` | `POST /api/v1/profile/mfa/setup`(先無 code 取 secret,再帶 code 確認) |
|
||||
| `mfa setup` | `POST /api/v1/profile/mfa/setup`(取 secret)→ `POST /api/v1/profile/mfa/setup/confirm`(帶 code 確認) |
|
||||
| `mfa disable` | `POST /api/v1/profile/mfa/disable` |
|
||||
| `mfa recovery-codes` | `POST /api/v1/profile/mfa/recovery-codes` |
|
||||
| `jwks list` | `GET /api/v1/jwks` |
|
||||
| `jwks show` | `GET /api/v1/jwks/{id}` |
|
||||
| `jwks create` | `POST /api/v1/jwks` |
|
||||
| `jwks toggle` | `POST /api/v1/jwks/{id}/toggle` |
|
||||
| `accounts list` | `GET /api/v1/accounts` |
|
||||
| `accounts show` | `GET /api/v1/accounts/{id}` |
|
||||
| `accounts create` | `POST /api/v1/accounts` |
|
||||
| `accounts update` | `PUT /api/v1/accounts/{id}` |
|
||||
| `accounts set-password` | `PUT /api/v1/accounts/{id}/password` |
|
||||
| `accounts delete` | `DELETE /api/v1/accounts/{id}` |
|
||||
| `audit-logs list` | `GET /api/v1/audit-logs` |
|
||||
|
||||
> `apps` 群組依賴 alterminal/bear#28 的 App 管理 JSON API(PAT Bearer、admin 限定);`profile` 系列依賴 alterminal/bear#35 的個人自助 JSON API(PAT Bearer、任何有效帳號)。
|
||||
|
||||
@@ -781,8 +768,8 @@ TIME CATEGORY EVENT ACTOR ACCOUN
|
||||
| `logout` | `POST /revoke`(撤銷 refresh token) | 僅清除本機憑證;PAT 需至網頁 `/profile/tokens` 撤銷 |
|
||||
| `status` | 顯示 access token 剩餘秒數 | 純本機判定:顯示模式(PAT/BEAR_TOKEN 環境變數)與 issuer |
|
||||
| `apps` | P3 暫緩 | ✅ 已實作(issue #10):`list`/`show`/`create`/`update`/`rotate-secret`/`toggle`,對接 alterminal/bear#28 的 `/api/v1/apps` JSON API(PAT Bearer、admin 限定)。`client_id` → UUID 由 CLI 以 list 解析;一次性 `client_secret` 僅於 create/rotate-secret 成功當下輸出 |
|
||||
| `jwks`/`accounts`/`audit-logs` | P3 暫緩 | ✔ 已實作(issue #12):對接 alterminal/bear#46 的管理端 JSON API(PAT Bearer、admin 限定);規格見 §3.8–§3.10 |
|
||||
| `profile`/`password`/`email`/`sessions`/`tokens`/`mfa` | P3 暫緩 | 未實作;個人自助指令規格見 #11(§3.7),伺服器端 API 見 alterminal/bear#35 |
|
||||
| `profile`/`password`/`email`/`sessions`/`tokens`/`mfa` | P3 暫緩 | ✅ 已實作(issue #11):`profile show/set`、`password change`、`email change`、`sessions list/revoke/revoke-others`、`tokens list/create/revoke`、`mfa status/setup/disable/recovery-codes`,對接 alterminal/bear#38 的 `/api/v1/profile*` JSON API(PAT Bearer、任何有效帳號)。敏感輸入互動提示;一次性明文(PAT 明文、recovery codes)僅於成功當下輸出;mfa setup 的 ASCII QR 由本機 eqrcode 生成 |
|
||||
| `jwks`/`accounts`/`audit-logs` | P3 暫緩 | ✅ 已實作(issue #12):對接 alterminal/bear#46 的管理端 JSON API(PAT Bearer、admin 限定);規格見 §3.8–§3.10 |
|
||||
|
||||
退出碼差異:`login`/`whoami` 的 token 無效(401)依 §3.1 為 `3`(本節實作一致)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user