# Kong DBAAS RBAC 鉴权分析与 Header Org 校验方案

## Summary

当前 `/api/v1/.../ngm/...` 路由走的是 `dbaas_auth` 插件，不是 `clinic` 插件。`dbaas_auth` 的流程在 `/Users/wlt/Documents/GolandProjects/clinic/cmd/api_gateway/tidbcloud/main.go`：初始化 account/RBAC 客户端，读取 `Authorization: Bearer <token>`，调用 TiDB Cloud account 校验用户，再通过 `rbac.CheckIdentityPermission` 校验 path 映射出的资源权限，成功后写入 `X-CLOUD-USER`。

后续需求只在 `dbaas_auth` 链路里加可选校验：Bearer token 校验成功后，若请求携带非空 `x-session-id`（该 header 实际保存的是 org id），优先从 path 的 `/clusters/{clusterID}` 提取 cluster id，path 中没有时再读取 `x-cluster-id`。然后直接查 Clinic 本地 DB 的 cluster 记录，将 `TenantID` 与 `x-session-id` 比较；一致才继续 RBAC。未携带 `x-session-id` 的请求跳过该项校验，继续现有 RBAC。

## Key Changes

- `dbaas_auth` 初始化 Clinic DB，配置和密码 secret 合并方式与 `clinic` 插件保持一致；不调用 management service。
- 在 Bearer token 校验成功后、`rbacChecker.Handle(kong, user)` 前新增 guard：
  - 先读取 `x-session-id`。为空时不做 cluster 查询，直接继续原 RBAC。
  - `x-session-id` 非空但不是合法 uint64 时返回 `403 forbidden`。
  - cluster id 优先从 path 的 `/clusters/{clusterID}` 片段提取；path 无该片段时才读取 `x-cluster-id`。
  - 使用 `model.GetAnyCluster(clusterID)` 查询本地 DB。
  - cluster id 缺失、cluster 不存在、查询失败，或 `cluster.TenantID != x-session-id` 时返回 `403 forbidden`。
  - 一致时继续现有 RBAC resource 匹配和 `CheckIdentityPermission` 流程。
- 保持 `clinic` 插件不变。

## Implementation Notes

- cluster org 反查封装在 `pkg/api_gateway/auth/tidbcloud` 下的小 helper，避免把 `cmd/api_gateway/tidbcloud/main.go` 继续撑大。
- `dbaas_auth` 主流程只负责调用 helper 并根据结果退出或继续，响应体沿用现有 `ExitResponse(16, "forbidden")`。
- 日志建议区分三类：missing header、invalid header、cluster org mismatch；不要打印 Bearer token。
- 每次校验记录必要的审计日志，固定包含 `trace_id`、`trace_source`、stage、`status`、`session_org_id`、`cluster_id`、cluster id 来源、`cluster_tenant_id`、DB 查询耗时和失败原因。携带 session 的请求会记录 DB 查询开始和最终结果；未携带 session 的请求只记录跳过结果。trace id 优先从 W3C `traceparent`、`X-Trace-ID`、B3、request id 读取；请求中没有 trace 时由 `dbaas_auth` 生成 32 位 trace id，并通过 `traceparent` 传给上游 NGM，使 Kong 鉴权日志与响应 `X-Trace-ID` 尽量保持一致。

## Test Plan

- `dbaas_auth` 单测：
  - Bearer token 有效，携带合法 `x-session-id` 且 org 匹配，继续调用 RBAC。
  - 未携带 `x-session-id`，跳过 DB 校验，继续调用 RBAC。
  - `x-session-id` 不是合法 uint64，返回 403。
  - 携带 `x-session-id` 时，cluster id 缺失、DB 查询失败或 cluster not found，返回 403。
  - cluster `TenantID` 与 header `x-session-id` 不一致，返回 403。
- RBAC resource 现有测试保持通过，确认 `/api/v1/orgs/.../clusters/.../ngm/slow_query/list` 仍映射到 `orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/ngm/slow_query/read`。
- 配置测试或 dry-run：确认 `dbaas-auth` overlay 渲染后包含 DB 连接配置和 `secret-config-path`。

## Assumptions

- 作用范围：仅 `dbaas_auth`。
- 反查来源：Clinic 本地 DB，使用 `model.GetAnyCluster(clusterID)` 返回的 `TenantID` 作为权威 org id。
- 失败策略：仅携带非空 `x-session-id` 的请求进入本校验；该 header 非法、cluster id 缺失、反查失败或不一致时 fail closed，统一返回 403。

## Current Implementation Status (2026-07-25)

The implementation intentionally differs from the original broad DB-validation proposal above. This section is the current source of truth for the deployed staging configuration.

- `x-session-id` validation is route-scoped through `session-id-validation.rules`, rather than applying to every `dbaas_auth` request. The public Data Proxy prefix (`^/api/v1/orgs/\\d+/data-proxy(?:/|$)`) and all seven new NGM Service B GET routes listed below are configured with `required: true`.
- A request without `x-session-id` to one of those new public routes is denied with 403 before Account RBAC. For routes outside these required rules, a missing header skips this additional validation and proceeds with the existing Bearer-token and Account RBAC flow. When a covered route has the header, the gateway validates the uint64 header value against `{org_id}` in the request path.
- The original cluster `TenantID` lookup is retained as clearly marked, commented-out code in `/Users/wlt/Documents/GolandProjects/clinic/pkg/api_gateway/auth/tidbcloud/org_cluster.go`. It is temporarily disabled, so the current validation does not query Clinic DB for a cluster TenantID. The corresponding log stage is emitted as skipped. Do not re-enable it without revisiting the shared-path caller policy.
- New Service B NGM public routes use a separate path group to avoid changing the existing Service A `GET /ngm/api/v1/...` behavior and its validation policy. The public paths are:
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/continuous_profiling/group_profiles`
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/continuous_profiling/group_profile/detail`
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/continuous_profiling/action_token`
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/continuous_profiling/download`
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/plan_replayer/list`
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/oom_record/list`
  - `GET /api/v1/orgs/{org_id}/ngm/clusters/{cluster_id}/oom_record/files`
- These NGM routes map in Kong to the existing Account resource `orgs/{org_id}/tidbs/{cluster_id}/common_org_owner`; no new Account privilege is required. The NGM service accepts the no-project path: it uses an incoming `x-project-id` when present, otherwise looks up `ProjectID` from the Clinic cluster record. The existing project-carrying NGM route remains compatible and continues to take project ID from the path.
- The public Data Proxy namespace also includes `GET /api/v1/orgs/{org_id}/data-proxy/metrics/{cluster_id}/query_range`, `GET /api/v1/orgs/{org_id}/data-proxy/metrics/{cluster_id}/series`, and `GET /api/v1/orgs/{org_id}/data-proxy/loki/{cluster_id}/query_range`. They reuse the canonical Metrics/Loki handlers, resolve ownership and targets server-side, and map to `common_org_owner`.
- Staging manifests and the external API gateway regex include the new path group. The full `cmd/api_gateway/tidbcloud` and `pkg/ngm` test packages remain blocked locally by unavailable private `github.com/tidbcloud/*` module credentials; focused file-download tests and staging Kustomize renders passed.
