265 lines
13 KiB
Markdown
265 lines
13 KiB
Markdown
# File Access Agent
|
||
|
||
File Access Agent 是檔案存取代理服務,用來控管 bucket / file space 裡檔案的上傳、下載、metadata 讀取與刪除權限。
|
||
|
||
本專案已決定使用 C# / ASP.NET Core。目標與授權模型依 `../member_center` 最新文件整理,後續實作會以此為準落地。
|
||
|
||
## 已確認決策
|
||
|
||
- 部署模型採 single-tenant instance:一個 File Access Agent instance 只服務一個 tenant。
|
||
- `tenant_id` 仍是 token 與授權邊界的一部分,但服務端會驗證其必須等於本 instance 的固定 tenant。
|
||
- 服務預設為無 DB、stateless access gateway。
|
||
- 每個 request 只處理單檔(upload/download/metadata/delete),不提供結構化查詢。
|
||
- 版本管理由上游服務負責;不同版本在 bucket 中視為不同單檔(不同 `object_key` 或等價識別)。
|
||
- Agent 可只以 `object_key` 或 `file_id` 作為單檔識別,不保存完整 metadata。
|
||
|
||
## 專案目的
|
||
|
||
此服務位於業務服務與實際檔案儲存 bucket 之間,負責檔案存取的安全邊界。
|
||
|
||
核心目的:
|
||
|
||
- 驗證由 Member Center 簽發的 OAuth2/JWT access token。
|
||
- 依 `scope`、`tenant_id`、`aud`、檔案識別與 HTTP method 判斷是否允許存取。
|
||
- 讓後端服務可以用 S2S token 上傳檔案。
|
||
- 讓後端服務在完成自身商業規則檢查後,向 Member Center 取得可給 client 使用的短效下載授權。
|
||
- 避免把一般 S2S access token 直接暴露給 client。
|
||
- 將 bucket provider 的直接存取權限集中在此服務或其 storage adapter 內。
|
||
|
||
## Agent 角色定義
|
||
|
||
File Access Agent 是 access permission gateway,不是檔案主資料服務。
|
||
|
||
負責:
|
||
|
||
- 驗證 token 與 request 邊界(`tenant_id`、`scope`、`aud`、`method`、`object_key/file_id`)
|
||
- 代理單檔 upload/download/metadata/delete
|
||
- 呼叫 Member Center delegated token validation endpoint
|
||
|
||
不負責:
|
||
|
||
- delegated token 簽發(由 Member Center 負責)
|
||
- 商業規則與版本管理(由上游服務負責)
|
||
- 結構化查詢、報表或資料分析
|
||
- metadata 主資料治理(預設不落地 DB)
|
||
|
||
## 與 Member Center 的關係
|
||
|
||
Member Center 是 OAuth2/OIDC issuer 與 JWKS 提供者。File Access Agent 不負責會員登入、OAuth client 管理或長效 access token 簽發。
|
||
|
||
`../member_center` 目前已規劃並落地下列 File Access 相關授權設定:
|
||
|
||
- audience / resource:`file_access_api`
|
||
- OAuth client usage:`file_api`
|
||
- scopes:
|
||
- `files:upload.write`
|
||
- `files:download.read`
|
||
- `files:download.delegate`
|
||
- `files:metadata.read`
|
||
- `files:delete`
|
||
|
||
最新版 `member_center` 文件已明確把 File Access 納入同一套租戶授權模型:`file_api` client 必須綁定 `tenant_id`,upload token 與 delegated download token 也都必須帶 `tenant_id`。
|
||
|
||
File Access Agent 應驗證:
|
||
|
||
- upload 的 S2S JWT:透過 Member Center JWKS 驗 `iss`、`aud=file_access_api`、`exp`、`scope`、`tenant_id`
|
||
- download 的 delegated token:以 Member Center validation endpoint 線上驗證
|
||
- token 中的 `tenant_id` 是否符合實際 request
|
||
- token 中的 `object_key` 或 `file_id` 是否符合實際 request
|
||
- token 中的 `method` 是否符合實際 request
|
||
|
||
待確認的檔案邊界 claim:
|
||
|
||
- `user_id`:delegated download token 目前由 Member Center 文件定義為必帶,用於對應業務服務已驗過的使用者身分。
|
||
- `owner_id` / `subject` / `client_id`:可用於紀錄或限定上傳者。
|
||
- `bucket` / `namespace` / `object_prefix`:若 storage 需要多 bucket 或 prefix 隔離,應優先明確化。
|
||
- `file_id` / `object_key`:下載 token 必須綁定其中一種檔案識別。
|
||
|
||
## 預期流程
|
||
|
||
### Upload: service to File Access Agent
|
||
|
||
1. 業務服務以 `client_credentials` 向 Member Center 取得 access token。
|
||
2. token 需包含 `aud=file_access_api`、`tenant_id` 與 `files:upload.write`。
|
||
3. 業務服務帶 Bearer token 呼叫 File Access Agent 上傳檔案。
|
||
4. File Access Agent 驗 token、`tenant_id` 與檔案邊界後,寫入 bucket / file space。
|
||
|
||
### Download: service to client to File Access Agent
|
||
|
||
1. client 向業務服務要求下載檔案。
|
||
2. 業務服務自行驗證商業規則與檔案權限。
|
||
3. 業務服務以 `files:download.delegate` 呼叫 Member Center `POST /file-access/download-tokens` 取得短效 download token。
|
||
4. download token 至少綁定 `tenant_id`、`user_id`、`object_key` 或 `file_id`、`method=GET`、短效 `exp`。
|
||
5. client 帶短效 token 直接向 File Access Agent 下載檔案。
|
||
6. File Access Agent 以 `files:download.read` 呼叫 Member Center `POST /file-access/download-tokens/validate`,帶上 token 與實際 GET request 邊界,驗證通過後放行。
|
||
|
||
責任邊界已確認:
|
||
|
||
- Member Center:簽發 upload 用的 S2S JWT、簽發 delegated short-lived download token、提供 download token validation endpoint。
|
||
- File Access Agent:驗證 upload JWT、呼叫 validation endpoint 驗 delegated download token、控管實際 bucket / file space 的讀寫。
|
||
- 業務服務:先驗商業規則與使用者檔案權限,再向 Member Center 申請 delegated download token。
|
||
|
||
## 初步 API 邊界
|
||
|
||
實際路由可在實作時調整,但能力邊界應維持清楚:
|
||
|
||
- `PUT /files/{object_key}`:上傳檔案,需 `files:upload.write`
|
||
- `GET /files/{object_key}`:下載檔案,需 `files:download.read`
|
||
- `HEAD /files/{object_key}` 或 `GET /files/metadata/{object_key}`:讀 metadata,需 `files:metadata.read`
|
||
- `DELETE /files/{object_key}`:刪除檔案,需 `files:delete`
|
||
|
||
本服務不負責提供 `POST /download-tokens`。該責任已在 Member Center:
|
||
|
||
- `POST /file-access/download-tokens`
|
||
- `POST /file-access/download-tokens/validate`
|
||
|
||
## 非目標
|
||
|
||
- 不負責會員註冊、登入、OAuth consent 或 OAuth client 管理。
|
||
- 不取代 Member Center 的 token issuer 職責。
|
||
- 不把 bucket credentials 發給 client。
|
||
- 不由 File Access Agent 判斷完整商業規則;業務服務仍需先判斷使用者是否可存取指定檔案。
|
||
- 不在語言與 storage provider 尚未決定前鎖死特定 SDK 或框架。
|
||
|
||
## 上傳命名與重複策略
|
||
|
||
- Agent 不做複雜命名規則;`object_key` 由介接服務決定。
|
||
- 相同 `object_key` 再次上傳時,Agent 視為覆蓋(overwrite),不自動版本化。
|
||
- 若要避免檔名重複,介接服務應自行把 `timestamp/uuid/業務主鍵` 放入 `object_key`。
|
||
- 若要版本管理,介接服務需為每個版本使用不同 `object_key`(例如 `v0001`, `v0002`)。
|
||
- 詳細命名建議請見 `docs/API.md` 的 Upload 區段。
|
||
|
||
## 待決策
|
||
|
||
- bucket / object storage provider,例如 S3、GCS、Cloudflare R2、MinIO 或本機檔案系統。
|
||
- 是否需要「可選」持久化:token `jti`、審計紀錄或特殊索引(預設不啟用)。
|
||
- object key 命名規則與 bucket / namespace / prefix 隔離策略。
|
||
- 檔案大小限制、content-type allowlist、病毒掃描與保留期限。
|
||
- 是否支援 presigned URL,或所有流量都經由 access agent proxy。
|
||
|
||
## Storage 設定
|
||
|
||
API 支援兩種 provider:
|
||
|
||
- `Storage:Provider=local`:使用本機檔案系統(`Instance:StorageRoot`)
|
||
- `Storage:Provider=minio`:使用 MinIO(S3 compatible)
|
||
|
||
MinIO 設定(`src/FileAccessAgent.Api/appsettings.json`):
|
||
|
||
- `Minio:Endpoint`:例如 `localhost:9000`
|
||
- `Minio:AccessKey`
|
||
- `Minio:SecretKey`
|
||
- `Minio:BucketName`
|
||
- `Minio:UseSsl`:`true|false`
|
||
- `Minio:Region`:可空
|
||
|
||
## Docker
|
||
|
||
API Dockerfile 位於 `src/FileAccessAgent.Api/Dockerfile`,建置時請使用 repo root 作為 build context,讓 Docker 可以存取 Api、Application、Domain、Infrastructure project references。
|
||
|
||
建置 image:
|
||
|
||
```bash
|
||
docker build -f src/FileAccessAgent.Api/Dockerfile -t file-access-agent-api .
|
||
```
|
||
|
||
啟動 API(container 內 listen `8080`):
|
||
|
||
```bash
|
||
docker run --rm -p 8080:8080 \
|
||
-e Instance__TenantId=d8107508-e6b9-4630-b982-c7bbb0cb228f \
|
||
-e Auth__Issuer=http://host.docker.internal:7850/ \
|
||
-e Auth__Audience=file_access_api \
|
||
-e Auth__RequireHttpsMetadata=false \
|
||
-e MemberCenter__BaseUrl=http://host.docker.internal:7850/ \
|
||
file-access-agent-api
|
||
```
|
||
|
||
若使用 local storage,建議掛載 volume,避免 container 移除後檔案遺失:
|
||
|
||
```bash
|
||
docker run --rm -p 8080:8080 \
|
||
-e Storage__Provider=local \
|
||
-e Instance__StorageRoot=/app/data/storage \
|
||
-v "$(pwd)/data/storage:/app/data/storage" \
|
||
file-access-agent-api
|
||
```
|
||
|
||
若使用 MinIO 且 MinIO 也在 Docker network 中,`Minio__Endpoint` 應使用 service/container DNS 名稱,例如 `minio:9000`;不要使用 container 內的 `localhost:9000`。
|
||
|
||
## 實作語言
|
||
|
||
本專案已決定使用 C# / ASP.NET Core。
|
||
|
||
### C# / ASP.NET Core
|
||
|
||
- 與 `member_center` 同語言,跨專案概念與維運一致。
|
||
- ASP.NET Core 對大檔串流、middleware、auth policy、DI、設定管理很成熟。
|
||
- 編譯期檢查較強,適合安全邊界服務。
|
||
- 可直接沿用 `member_center` 的設定、OpenAPI、JWT / OAuth 與部署習慣。
|
||
|
||
目前實作方向會以 ASP.NET Core Web API 為主,並保留 storage adapter 抽象,避免過早綁死特定 bucket provider。
|
||
|
||
## 文件維護規則
|
||
|
||
每次改動功能、流程、API、scope、設定或部署方式時,都要同步更新文件。
|
||
|
||
優先更新順序:
|
||
|
||
1. `README.md`:專案目的、目前決策、執行方式與重要限制。
|
||
2. `docs/`:若後續建立更細文件,放設計、API、流程、部署、決策紀錄。
|
||
3. API 規格:一旦路由與 payload 穩定,補 OpenAPI 或等價規格。
|
||
|
||
如果程式碼與文件不一致,應先修正 README 或在 README 標示尚未決定,避免讓文件變成過期假設。
|
||
|
||
目前文件:
|
||
|
||
- `docs/API.md`:File Access Agent API 規格與 Member Center 整合契約
|
||
- `docs/openapi.yaml`:本專案 OpenAPI 草案
|
||
- `docs/DATA_MODEL.md`:可選持久化擴充(非預設)
|
||
|
||
## 測試專案
|
||
|
||
- `src/FileAccessAgent.TestSite`:Redirect login 測試站(OIDC Authorization Code + PKCE)
|
||
- 目的:透過 Member Center `web_login` client 登入,取得並顯示 token/claims,並直接測試 Agent API(upload / download / head / metadata / delete / health)
|
||
- 測試站採雙 token 模式:
|
||
- `web_login` token:只用於身份展示(claims / user context)
|
||
- `file_api` service token(client credentials):用於呼叫 Agent 與 Member Center delegated token API
|
||
|
||
最小設定(`src/FileAccessAgent.TestSite/appsettings.json`):
|
||
|
||
- `MemberCenter:Authority`
|
||
- `MemberCenter:WebBaseUrl`(Member Center Web,預設 `http://localhost:5080`,供 redirect logout 使用)
|
||
- `MemberCenter:ClientId`(web_login)
|
||
- `MemberCenter:ClientSecret`(web_login public client 可留空)
|
||
- `MemberCenter:CallbackPath`(需與 Member Center OAuth client redirect URI 一致)
|
||
- `MemberCenter:SignedOutCallbackPath`(OIDC middleware callback,建議保留預設 `/signout-callback-oidc`,不要與 direct logout callback 共用)
|
||
- `MemberCenter:Scopes`(建議 `openid email profile`)
|
||
- `MemberCenter:ServiceClientId`(file_api)
|
||
- `MemberCenter:ServiceClientSecret`(file_api)
|
||
- `MemberCenter:ServiceScopes`(至少 `files:upload.write files:metadata.read files:delete files:download.delegate`)
|
||
- `FileAccessAgent:BaseUrl`
|
||
- `FileAccessAgent:TenantId`
|
||
|
||
啟動:
|
||
|
||
- `dotnet run --project src/FileAccessAgent.TestSite/FileAccessAgent.TestSite.csproj`
|
||
- 進入首頁後點 `Login`,會導到 Member Center,再 redirect 回測試站
|
||
- `Logout` 會走 Member Center Web `GET /account/logout?returnUrl=...`,再 callback 回 TestSite `/auth/logout-callback`
|
||
- 若 logout 後未回到 TestSite,而停在 Member Center,需在 Member Center Web 設定:
|
||
- `Auth__AllowedLogoutReturnUrlPrefixes=http://localhost:5091/`
|
||
- 登入後可在同一頁直接執行 `Upload`、`Download`、`Head`、`Get Metadata`、`Delete`、`Health`
|
||
- `Upload` 改為檔案挑選上傳;`Object Key` 可選填(留空會自動產生 `demo/uploads/<timestamp>-<filename>`)
|
||
- 上傳成功後會保留回傳的 `object_key` 供後續 `Metadata/Head/Download/Delete` 直接測試
|
||
- 另提供 `Download (Invalid Token)` 測試,故意帶錯 token 驗證 Agent 拒絕邏輯
|
||
- `Download` 會先呼叫 Member Center `POST /file-access/download-tokens` 申請 delegated token,再帶 token 呼叫 Agent 下載
|
||
- `Download File` 走 sample 的直連模式:TestSite 僅向 Member Center 取 delegated token,接著把 client 轉址到 Agent 下載 URL(含短效 `access_token`),下載流量不經 TestSite
|
||
|
||
## 目前狀態
|
||
|
||
- 專案目標已依 `../member_center` 文件整理。
|
||
- 已確認使用 C# / ASP.NET Core。
|
||
- `tenant_id` 與 delegated download token 分工已依最新版 `member_center` 文件校正。
|
||
- API 已提供 Dockerfile,可用 .NET 8 多階段建置產生 container image。
|
||
- 目前目錄中的 .NET skeleton 可作為正式起點,但仍需依最新流程補上 Member Center validation 串接。
|