mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 01:47:47 +08:00
Fix native Antigravity stream envelope handling
This commit is contained in:
@@ -16,16 +16,48 @@ use crate::{AppState, GatewayError};
|
||||
use super::super::GatewayControlDecision;
|
||||
use super::credentials::{
|
||||
build_auth_context_cache_key, current_unix_secs, extract_request_credentials,
|
||||
extract_trusted_admin_headers,
|
||||
extract_trusted_admin_headers, hash_api_key,
|
||||
};
|
||||
use super::gate::GatewayLocalAuthRejection;
|
||||
use super::principal::derive_principal_candidate;
|
||||
use super::types::{GatewayPrincipalCandidate, GatewayTrustedAuthHeaders};
|
||||
use super::types::{
|
||||
GatewayCredentialCarrier, GatewayPrincipalCandidate, GatewayTrustedAuthHeaders,
|
||||
};
|
||||
use crate::headers::header_value_str;
|
||||
|
||||
const AUTH_CONTEXT_CACHE_TTL: Duration = Duration::from_secs(60);
|
||||
const AUTH_CONTEXT_CACHE_MAX_ENTRIES: usize = 256;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct AntigravityBearerBridgeConfig {
|
||||
#[serde(default)]
|
||||
enabled: bool,
|
||||
#[serde(default)]
|
||||
auth_user_id: String,
|
||||
#[serde(default)]
|
||||
auth_api_key_id: String,
|
||||
#[serde(default)]
|
||||
bearer_sha256_allowlist: Vec<String>,
|
||||
#[serde(default)]
|
||||
allow_unverified_google_bearer: bool,
|
||||
}
|
||||
|
||||
impl AntigravityBearerBridgeConfig {
|
||||
fn bearer_validation_mode(&self, raw_bearer: &str) -> Option<&'static str> {
|
||||
if !self.bearer_sha256_allowlist.is_empty() {
|
||||
let bearer_hash = hash_api_key(raw_bearer);
|
||||
return self
|
||||
.bearer_sha256_allowlist
|
||||
.iter()
|
||||
.any(|allowed| allowed.trim().eq_ignore_ascii_case(&bearer_hash))
|
||||
.then_some("sha256_allowlist");
|
||||
}
|
||||
|
||||
self.allow_unverified_google_bearer
|
||||
.then_some("explicit_unverified")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct GatewayControlAuthContext {
|
||||
pub(crate) user_id: String,
|
||||
@@ -607,14 +639,117 @@ pub(super) async fn resolve_data_backed_auth_context(
|
||||
.await,
|
||||
))
|
||||
}
|
||||
Some(
|
||||
GatewayPrincipalCandidate::DeferredBearerToken { .. }
|
||||
| GatewayPrincipalCandidate::DeferredCookieHeader { .. },
|
||||
) => Ok(None),
|
||||
Some(GatewayPrincipalCandidate::DeferredBearerToken { raw, carrier }) => {
|
||||
if let Some(auth_context) = resolve_antigravity_bearer_bridge_auth_context(
|
||||
state,
|
||||
signature,
|
||||
raw.as_str(),
|
||||
carrier,
|
||||
now_unix_secs,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
return Ok(Some(auth_context));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
Some(GatewayPrincipalCandidate::DeferredCookieHeader { .. }) => Ok(None),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve_antigravity_bearer_bridge_auth_context(
|
||||
state: &AppState,
|
||||
auth_endpoint_signature: &str,
|
||||
raw_bearer: &str,
|
||||
carrier: GatewayCredentialCarrier,
|
||||
now_unix_secs: u64,
|
||||
) -> Result<Option<GatewayControlAuthContext>, GatewayError> {
|
||||
if carrier != GatewayCredentialCarrier::AuthorizationBearer
|
||||
|| !auth_endpoint_signature
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("antigravity:v1internal")
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(config_value) = state
|
||||
.read_system_config_json_value(crate::constants::ANTIGRAVITY_BEARER_BRIDGE_CONFIG_KEY)
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if config_value.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let config: AntigravityBearerBridgeConfig =
|
||||
serde_json::from_value(config_value).map_err(|err| {
|
||||
GatewayError::Internal(format!(
|
||||
"{} invalid: {err}",
|
||||
crate::constants::ANTIGRAVITY_BEARER_BRIDGE_CONFIG_KEY
|
||||
))
|
||||
})?;
|
||||
if !config.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(validation_mode) = config.bearer_validation_mode(raw_bearer) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let user_id = config.auth_user_id.trim();
|
||||
let api_key_id = config.auth_api_key_id.trim();
|
||||
if user_id.is_empty() || api_key_id.is_empty() {
|
||||
return Err(GatewayError::Internal(format!(
|
||||
"{} requires auth_user_id and auth_api_key_id",
|
||||
crate::constants::ANTIGRAVITY_BEARER_BRIDGE_CONFIG_KEY
|
||||
)));
|
||||
}
|
||||
|
||||
let snapshot = state
|
||||
.data
|
||||
.read_auth_api_key_snapshot(user_id, api_key_id, now_unix_secs)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
||||
let Some(snapshot) = snapshot else {
|
||||
return Ok(Some(GatewayControlAuthContext {
|
||||
user_id: user_id.to_string(),
|
||||
api_key_id: api_key_id.to_string(),
|
||||
username: None,
|
||||
api_key_name: None,
|
||||
balance_remaining: None,
|
||||
access_allowed: false,
|
||||
user_rate_limit: None,
|
||||
api_key_rate_limit: None,
|
||||
api_key_is_standalone: false,
|
||||
admin_bypass_limits: false,
|
||||
local_rejection: Some(GatewayLocalAuthRejection::InvalidApiKey),
|
||||
allowed_models: None,
|
||||
ip_rules: None,
|
||||
}));
|
||||
};
|
||||
|
||||
let wallet_access = resolve_wallet_auth_gate(state, &snapshot).await?;
|
||||
let auth_context = build_data_backed_auth_context(
|
||||
state,
|
||||
snapshot,
|
||||
auth_endpoint_signature,
|
||||
None,
|
||||
None,
|
||||
wallet_access,
|
||||
)
|
||||
.await;
|
||||
info!(
|
||||
event_name = "antigravity_bearer_bridge_auth_context_resolved",
|
||||
log_type = "event",
|
||||
validation_mode,
|
||||
user_id = auth_context.user_id.as_str(),
|
||||
api_key_id = auth_context.api_key_id.as_str(),
|
||||
access_allowed = auth_context.access_allowed,
|
||||
has_local_rejection = auth_context.local_rejection.is_some(),
|
||||
"resolved Antigravity bearer bridge auth context"
|
||||
);
|
||||
Ok(Some(auth_context))
|
||||
}
|
||||
|
||||
async fn resolve_trusted_auth_context(
|
||||
state: &AppState,
|
||||
auth_endpoint_signature: &str,
|
||||
|
||||
Reference in New Issue
Block a user