perf: improve compression reliability and deployment safety
This commit is contained in:
@@ -18,8 +18,14 @@ use uuid::Uuid;
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum Principal {
|
||||
Anonymous { session_id: String },
|
||||
User { user_id: Uuid, role: String, email_verified: bool },
|
||||
Anonymous {
|
||||
session_id: String,
|
||||
},
|
||||
User {
|
||||
user_id: Uuid,
|
||||
role: String,
|
||||
email_verified: bool,
|
||||
},
|
||||
ApiKey {
|
||||
user_id: Uuid,
|
||||
api_key_id: Uuid,
|
||||
@@ -29,6 +35,14 @@ pub enum Principal {
|
||||
}
|
||||
|
||||
pub fn client_ip(headers: &HeaderMap, connect_ip: IpAddr) -> IpAddr {
|
||||
resolve_client_ip(headers, connect_ip, crate::config::trust_proxy_headers())
|
||||
}
|
||||
|
||||
fn resolve_client_ip(headers: &HeaderMap, connect_ip: IpAddr, trust_proxy: bool) -> IpAddr {
|
||||
if !trust_proxy {
|
||||
return connect_ip;
|
||||
}
|
||||
|
||||
if let Some(ip) = parse_forwarded_for(headers) {
|
||||
return ip;
|
||||
}
|
||||
@@ -69,7 +83,13 @@ pub async fn authenticate(
|
||||
return Err(AppError::new(ErrorCode::Unauthorized, "未登录"));
|
||||
}
|
||||
|
||||
let (jar, session_id) = ensure_session_cookie(jar);
|
||||
let cookie_secure = state
|
||||
.config
|
||||
.public_base_url
|
||||
.trim()
|
||||
.to_ascii_lowercase()
|
||||
.starts_with("https://");
|
||||
let (jar, session_id) = ensure_session_cookie(jar, cookie_secure);
|
||||
Ok((jar, Principal::Anonymous { session_id }))
|
||||
}
|
||||
|
||||
@@ -178,11 +198,12 @@ async fn try_api_key(
|
||||
return Err(AppError::new(ErrorCode::Unauthorized, "API Key 无效"));
|
||||
}
|
||||
|
||||
let _ = sqlx::query("UPDATE api_keys SET last_used_at = NOW(), last_used_ip = $2 WHERE id = $1")
|
||||
.bind(row.id)
|
||||
.bind(ip.to_string())
|
||||
.execute(&state.db)
|
||||
.await;
|
||||
let _ =
|
||||
sqlx::query("UPDATE api_keys SET last_used_at = NOW(), last_used_ip = $2 WHERE id = $1")
|
||||
.bind(row.id)
|
||||
.bind(ip.to_string())
|
||||
.execute(&state.db)
|
||||
.await;
|
||||
|
||||
Ok(Some(Principal::ApiKey {
|
||||
user_id: row.user_id,
|
||||
@@ -192,7 +213,7 @@ async fn try_api_key(
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn ensure_session_cookie(jar: CookieJar) -> (CookieJar, String) {
|
||||
pub fn ensure_session_cookie(jar: CookieJar, secure: bool) -> (CookieJar, String) {
|
||||
if let Some(cookie) = jar.get("if_session") {
|
||||
let session_id = cookie.value().trim().to_string();
|
||||
if !session_id.is_empty() {
|
||||
@@ -204,6 +225,7 @@ pub fn ensure_session_cookie(jar: CookieJar) -> (CookieJar, String) {
|
||||
let cookie = Cookie::build(("if_session", session_id.clone()))
|
||||
.path("/")
|
||||
.http_only(true)
|
||||
.secure(secure)
|
||||
.same_site(SameSite::Lax)
|
||||
.max_age(TimeDuration::days(7))
|
||||
.build();
|
||||
@@ -220,10 +242,37 @@ fn generate_session_id() -> String {
|
||||
pub fn api_key_hash(full_key: &str, pepper: &str) -> Result<String, AppError> {
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(pepper.as_bytes())
|
||||
.map_err(|err| AppError::new(ErrorCode::Internal, "API Key pepper 错误").with_source(err))?;
|
||||
let mut mac = HmacSha256::new_from_slice(pepper.as_bytes()).map_err(|err| {
|
||||
AppError::new(ErrorCode::Internal, "API Key pepper 错误").with_source(err)
|
||||
})?;
|
||||
mac.update(full_key.as_bytes());
|
||||
let result = mac.finalize().into_bytes();
|
||||
Ok(hex::encode(result))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn untrusted_proxy_headers_cannot_override_peer_ip() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-forwarded-for", "203.0.113.9".parse().unwrap());
|
||||
let peer = "192.0.2.10".parse().unwrap();
|
||||
assert_eq!(resolve_client_ip(&headers, peer, false), peer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_proxy_headers_use_forwarded_client_ip() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
"x-forwarded-for",
|
||||
"203.0.113.9, 192.0.2.20".parse().unwrap(),
|
||||
);
|
||||
let peer = "192.0.2.10".parse().unwrap();
|
||||
assert_eq!(
|
||||
resolve_client_ip(&headers, peer, true),
|
||||
"203.0.113.9".parse::<IpAddr>().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user