mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
fix(api): resolve the admin request id independently of the header name
The request-id layer injects under the configured api.request_id_header, but the admin extractor read the hardcoded x-request-id name, so pointing that setting at any other header made every authenticated admin request fail with an internal error and took the admin interface down. Read the name-agnostic RequestId extension the layer also populates, keeping the header lookup as a fallback for callers that run without the layer. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ use gateway_admin::{
|
||||
model::auth::{AdminPrincipal, AdminRequestContext, LoginCommand, LoginError},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_http::request_id::RequestId;
|
||||
|
||||
use super::{AdminEnvelope, AdminError, AdminResponse};
|
||||
|
||||
@@ -51,22 +52,32 @@ where
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let principal = require_admin_auth(state, &parts.headers).await?;
|
||||
let request_id = parts
|
||||
.headers
|
||||
.get(REQUEST_ID_HEADER)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
let request_id = admin_request_id(parts)
|
||||
.ok_or_else(|| AdminError::internal("Missing admin request context"))?;
|
||||
Ok(Self {
|
||||
context: AdminRequestContext {
|
||||
principal,
|
||||
request_id: request_id.to_owned(),
|
||||
request_id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// request-id 层按配置的 header 名注入,同时写入与名字无关的扩展;
|
||||
/// 优先读扩展,使自定义 header 名不会让管理请求失去请求上下文。
|
||||
/// header 回退覆盖未装配该层的调用方。
|
||||
fn admin_request_id(parts: &Parts) -> Option<String> {
|
||||
parts
|
||||
.extensions
|
||||
.get::<RequestId>()
|
||||
.map(RequestId::header_value)
|
||||
.or_else(|| parts.headers.get(REQUEST_ID_HEADER))
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
pub async fn require_admin_session<S>(state: &S, headers: &HeaderMap) -> Result<String, AdminError>
|
||||
where
|
||||
S: AdminSessionState + Send + Sync,
|
||||
|
||||
@@ -213,3 +213,26 @@ async fn settings_should_accept_admin_api_key_header() {
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn admin_auth_should_accept_a_configured_request_id_header_name() {
|
||||
use axum::http::HeaderName;
|
||||
use tower_http::request_id::{MakeRequestUuid, SetRequestIdLayer};
|
||||
|
||||
let fixture = AdminTestFixture::new().await;
|
||||
fixture.auth.insert_session("valid-session");
|
||||
// 部署把 api.request_id_header 改名后,注入的 header 不再叫 x-request-id;
|
||||
// 管理请求仍须拿到请求上下文,而不是退化为 500。
|
||||
let custom = HeaderName::from_static("x-trace-id");
|
||||
let app = app(fixture.state()).layer(SetRequestIdLayer::new(custom, MakeRequestUuid));
|
||||
let unlabelled = Request::builder()
|
||||
.method(Method::GET)
|
||||
.uri("/api/admin/settings")
|
||||
.header(header::COOKIE, "cpr_admin_session=valid-session")
|
||||
.body(Body::empty())
|
||||
.expect("build settings request");
|
||||
|
||||
let response = app.oneshot(unlabelled).await.expect("settings response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,8 +23,8 @@ Client Key 固定绑定一个 Provider:`openai` 或 `xai`。同一次请求不
|
||||
- `x-api-key: <admin-api-key>`。
|
||||
|
||||
请求无需自带 `x-request-id`;缺失时服务端自动生成 UUID 并在响应头回传同一 request ID。
|
||||
`api.request_id_header` 只改变注入与回传的 header 名,而管理端鉴权固定读取 `x-request-id`,
|
||||
因此启用管理端时该配置必须保持默认值。管理端响应统一带 `Cache-Control: no-store`。
|
||||
`api.request_id_header` 可改变注入与回传的 header 名,管理端鉴权不依赖该名字。
|
||||
管理端响应统一带 `Cache-Control: no-store`。
|
||||
|
||||
配置了 CORS 白名单 origin 时,跨域请求以凭据模式放行,仅允许 `GET`/`POST` 方法和
|
||||
`authorization`、`content-type`、`x-api-key` 与 request ID 四个请求头,不使用通配符。
|
||||
|
||||
Reference in New Issue
Block a user