mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
Support per-format provider key auth
This commit is contained in:
@@ -463,6 +463,8 @@ mod tests {
|
||||
auth_type: "api_key".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -483,6 +483,8 @@ mod tests {
|
||||
auth_type: "oauth".to_string(),
|
||||
is_active: true,
|
||||
api_formats: None,
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
@@ -539,6 +541,8 @@ mod tests {
|
||||
auth_type: "api_key".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -1882,6 +1882,8 @@ mod tests {
|
||||
auth_type: "api_key".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -59,6 +59,8 @@ fn sample_transport(base_url: &str, api_format: &str) -> GatewayProviderTranspor
|
||||
auth_type: "oauth".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec![api_format.to_string()]),
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -30,7 +30,10 @@ pub(super) fn admin_monitoring_masked_provider_key_prefix(
|
||||
"service_account" | "vertex_ai" => Some("[Service Account]".to_string()),
|
||||
"oauth" => Some("[OAuth Token]".to_string()),
|
||||
_ => {
|
||||
let full_key = admin_monitoring_try_decrypt_secret(state, &key.encrypted_api_key)?;
|
||||
let full_key = key
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.and_then(|ciphertext| admin_monitoring_try_decrypt_secret(state, ciphertext))?;
|
||||
if full_key.len() <= 12 {
|
||||
Some(format!("{full_key}***"))
|
||||
} else {
|
||||
|
||||
@@ -168,7 +168,7 @@ pub(crate) fn mapping_preview_masked_catalog_api_key(
|
||||
state: &AdminAppState<'_>,
|
||||
key: &StoredProviderCatalogKey,
|
||||
) -> String {
|
||||
let ciphertext = key.encrypted_api_key.trim();
|
||||
let ciphertext = key.encrypted_api_key.as_deref().unwrap_or("").trim();
|
||||
if ciphertext.is_empty() {
|
||||
return "***".to_string();
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ pub(crate) async fn update_existing_provider_oauth_catalog_key(
|
||||
.map(|duration| duration.as_secs())
|
||||
.unwrap_or(0);
|
||||
let mut updated = existing_key.clone();
|
||||
updated.encrypted_api_key = encrypted_api_key;
|
||||
updated.encrypted_api_key = Some(encrypted_api_key);
|
||||
updated.encrypted_auth_config = Some(encrypted_auth_config);
|
||||
updated.api_formats = provider_oauth_catalog_key_api_formats(provider_type, api_formats);
|
||||
updated.is_active = true;
|
||||
|
||||
@@ -831,6 +831,10 @@ pub(super) fn build_admin_pool_key_payload(
|
||||
payload.insert("key_name".to_string(), json!(key.name));
|
||||
payload.insert("is_active".to_string(), json!(key.is_active));
|
||||
payload.insert("auth_type".to_string(), json!(key.auth_type));
|
||||
payload.insert(
|
||||
"auth_type_by_format".to_string(),
|
||||
json!(key.auth_type_by_format),
|
||||
);
|
||||
payload.insert(
|
||||
"credential_kind".to_string(),
|
||||
json!(auth_semantics.credential_kind().as_str()),
|
||||
|
||||
@@ -98,6 +98,7 @@ pub(super) async fn build_admin_pool_resolve_selection_response(
|
||||
"key_id": key.id,
|
||||
"key_name": key.name,
|
||||
"auth_type": key.auth_type,
|
||||
"auth_type_by_format": key.auth_type_by_format,
|
||||
"credential_kind": auth_semantics.credential_kind().as_str(),
|
||||
"runtime_auth_kind": auth_semantics.runtime_auth_kind().as_str(),
|
||||
"oauth_managed": auth_semantics.oauth_managed(),
|
||||
|
||||
@@ -12,6 +12,8 @@ pub(crate) struct AdminProviderKeyCreateRequest {
|
||||
#[serde(default)]
|
||||
pub(crate) auth_type: Option<String>,
|
||||
#[serde(default)]
|
||||
pub(crate) auth_type_by_format: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub(crate) auth_config: Option<serde_json::Value>,
|
||||
pub(crate) name: String,
|
||||
#[serde(default)]
|
||||
@@ -49,6 +51,8 @@ pub(crate) struct AdminProviderKeyUpdateRequest {
|
||||
#[serde(default)]
|
||||
pub(crate) auth_type: Option<String>,
|
||||
#[serde(default)]
|
||||
pub(crate) auth_type_by_format: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub(crate) auth_config: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub(crate) name: Option<String>,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::handlers::admin::provider::shared::payloads::AdminProviderKeyCreateRequest;
|
||||
use crate::handlers::admin::provider::write::normalize::{
|
||||
normalize_api_format_json_object_keys, normalize_api_format_list, normalize_auth_type,
|
||||
validate_vertex_api_formats,
|
||||
normalize_auth_type_by_format, validate_vertex_api_formats,
|
||||
};
|
||||
use crate::handlers::admin::request::AdminAppState;
|
||||
use crate::handlers::admin::shared::{
|
||||
@@ -33,6 +33,15 @@ pub(crate) async fn build_admin_create_provider_key_record(
|
||||
);
|
||||
let auth_type = normalize_auth_type(payload.auth_type.as_deref())?;
|
||||
validate_vertex_api_formats(&provider.provider_type, &auth_type, &api_formats)?;
|
||||
let auth_type_by_format = if matches!(auth_type.as_str(), "api_key" | "bearer") {
|
||||
normalize_auth_type_by_format(
|
||||
payload.auth_type_by_format,
|
||||
"auth_type_by_format",
|
||||
&api_formats,
|
||||
)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let api_key = payload.api_key.unwrap_or_default().trim().to_string();
|
||||
let auth_config = normalize_json_object(payload.auth_config, "auth_config")?;
|
||||
@@ -42,9 +51,6 @@ pub(crate) async fn build_admin_create_provider_key_record(
|
||||
.cloned();
|
||||
|
||||
match auth_type.as_str() {
|
||||
"api_key" if api_key.is_empty() => {
|
||||
return Err("API Key 认证模式下 api_key 为必填字段".to_string());
|
||||
}
|
||||
"service_account" if auth_config_object.is_none() => {
|
||||
return Err("Service Account 认证模式下 auth_config 为必填字段".to_string());
|
||||
}
|
||||
@@ -59,15 +65,18 @@ pub(crate) async fn build_admin_create_provider_key_record(
|
||||
.await
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
|
||||
if auth_type == "api_key" {
|
||||
if matches!(auth_type.as_str(), "api_key" | "bearer") && !api_key.is_empty() {
|
||||
for existing in existing_keys
|
||||
.iter()
|
||||
.filter(|existing| existing.auth_type.trim().eq_ignore_ascii_case("api_key"))
|
||||
.filter(|existing| raw_secret_auth_type(&existing.auth_type))
|
||||
{
|
||||
let Some(decrypted) = decrypt_catalog_secret_with_fallbacks(
|
||||
state.encryption_key(),
|
||||
&existing.encrypted_api_key,
|
||||
) else {
|
||||
let Some(decrypted) = existing
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.and_then(|ciphertext| {
|
||||
decrypt_catalog_secret_with_fallbacks(state.encryption_key(), ciphertext)
|
||||
})
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if decrypted != "__placeholder__" && decrypted == api_key {
|
||||
@@ -116,10 +125,12 @@ pub(crate) async fn build_admin_create_provider_key_record(
|
||||
}
|
||||
|
||||
let encrypted_api_key = match auth_type.as_str() {
|
||||
"api_key" => encrypt_catalog_secret_with_fallbacks(state, &api_key),
|
||||
_ => encrypt_catalog_secret_with_fallbacks(state, "__placeholder__"),
|
||||
}
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?;
|
||||
"api_key" | "bearer" if !api_key.is_empty() => Some(
|
||||
encrypt_catalog_secret_with_fallbacks(state, &api_key)
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?,
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let encrypted_auth_config = auth_config
|
||||
.as_ref()
|
||||
@@ -180,7 +191,15 @@ pub(crate) async fn build_admin_create_provider_key_record(
|
||||
normalize_string_list(payload.model_exclude_patterns).map(|value| json!(value));
|
||||
key.health_by_format = Some(json!({}));
|
||||
key.circuit_breaker_by_format = Some(json!({}));
|
||||
key.auth_type_by_format = auth_type_by_format;
|
||||
key.created_at_unix_ms = Some(now_unix_secs);
|
||||
key.updated_at_unix_secs = Some(now_unix_secs);
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
fn raw_secret_auth_type(value: &str) -> bool {
|
||||
matches!(
|
||||
value.trim().to_ascii_lowercase().as_str(),
|
||||
"api_key" | "bearer"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::handlers::admin::provider::shared::payloads::AdminProviderKeyUpdatePatch;
|
||||
use crate::handlers::admin::provider::write::normalize::{
|
||||
normalize_api_format_json_object_keys, normalize_api_format_list, normalize_auth_type,
|
||||
validate_vertex_api_formats,
|
||||
normalize_auth_type_by_format, validate_vertex_api_formats,
|
||||
};
|
||||
use crate::handlers::admin::request::AdminAppState;
|
||||
use crate::handlers::admin::shared::{
|
||||
@@ -48,10 +48,6 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.map(ToOwned::to_owned);
|
||||
if api_key_present && api_key_value.as_deref() == Some("") {
|
||||
return Err("api_key 不能为空".to_string());
|
||||
}
|
||||
|
||||
let auth_config_present = fields.contains("auth_config");
|
||||
let auth_config = normalize_json_object(payload.auth_config, "auth_config")?;
|
||||
let auth_config_object = auth_config
|
||||
@@ -65,31 +61,26 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
|
||||
match target_auth_type.as_str() {
|
||||
"api_key" => {
|
||||
if auth_type_switch
|
||||
&& matches!(
|
||||
api_key_value.as_deref(),
|
||||
None | Some("") | Some("__placeholder__")
|
||||
)
|
||||
"api_key" | "bearer" => {
|
||||
if let Some(api_key) = api_key_value
|
||||
.as_deref()
|
||||
.filter(|value| !value.is_empty() && *value != "__placeholder__")
|
||||
{
|
||||
return Err("切换到 API Key 认证模式时,必须提供新的 API Key".to_string());
|
||||
}
|
||||
if api_key_present
|
||||
&& matches!(
|
||||
api_key_value.as_deref(),
|
||||
None | Some("") | Some("__placeholder__")
|
||||
)
|
||||
{
|
||||
return Err("API Key 认证模式下 api_key 不能为空".to_string());
|
||||
}
|
||||
if let Some(api_key) = api_key_value.as_deref() {
|
||||
for existing_key in existing_keys.iter().filter(|key| {
|
||||
key.id != existing.id && key.auth_type.trim().eq_ignore_ascii_case("api_key")
|
||||
}) {
|
||||
let Some(decrypted) = decrypt_catalog_secret_with_fallbacks(
|
||||
state.encryption_key(),
|
||||
&existing_key.encrypted_api_key,
|
||||
) else {
|
||||
for existing_key in existing_keys
|
||||
.iter()
|
||||
.filter(|key| key.id != existing.id && raw_secret_auth_type(&key.auth_type))
|
||||
{
|
||||
let Some(decrypted) =
|
||||
existing_key
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.and_then(|ciphertext| {
|
||||
decrypt_catalog_secret_with_fallbacks(
|
||||
state.encryption_key(),
|
||||
ciphertext,
|
||||
)
|
||||
})
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if decrypted != "__placeholder__" && decrypted == api_key {
|
||||
@@ -99,9 +90,12 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
));
|
||||
}
|
||||
}
|
||||
updated.encrypted_api_key =
|
||||
updated.encrypted_api_key = Some(
|
||||
encrypt_catalog_secret_with_fallbacks(state, api_key)
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?;
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?,
|
||||
);
|
||||
} else if api_key_present {
|
||||
updated.encrypted_api_key = None;
|
||||
}
|
||||
updated.encrypted_auth_config = None;
|
||||
}
|
||||
@@ -120,9 +114,7 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
return Err("Service Account 认证模式下不允许直接填写 api_key".to_string());
|
||||
}
|
||||
if auth_type_switch || api_key_present {
|
||||
updated.encrypted_api_key =
|
||||
encrypt_catalog_secret_with_fallbacks(state, "__placeholder__")
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?;
|
||||
updated.encrypted_api_key = None;
|
||||
}
|
||||
if let Some(client_email) = auth_config_object
|
||||
.as_ref()
|
||||
@@ -181,9 +173,7 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
return Err("OAuth 认证模式下不允许直接填写 api_key".to_string());
|
||||
}
|
||||
if auth_type_switch {
|
||||
updated.encrypted_api_key =
|
||||
encrypt_catalog_secret_with_fallbacks(state, "__placeholder__")
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?;
|
||||
updated.encrypted_api_key = None;
|
||||
updated.encrypted_auth_config = None;
|
||||
}
|
||||
}
|
||||
@@ -197,6 +187,7 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
);
|
||||
if managed_fixed_oauth_key {
|
||||
updated.api_formats = None;
|
||||
updated.auth_type_by_format = None;
|
||||
} else {
|
||||
validate_vertex_api_formats(&provider.provider_type, &target_auth_type, &api_formats)?;
|
||||
updated.api_formats = Some(json!(api_formats));
|
||||
@@ -211,6 +202,26 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
}
|
||||
}
|
||||
|
||||
let effective_api_formats =
|
||||
normalize_api_format_list(json_string_list(updated.api_formats.as_ref()));
|
||||
if matches!(target_auth_type.as_str(), "api_key" | "bearer") {
|
||||
if fields.contains("auth_type_by_format") {
|
||||
updated.auth_type_by_format = normalize_auth_type_by_format(
|
||||
payload.auth_type_by_format,
|
||||
"auth_type_by_format",
|
||||
&effective_api_formats,
|
||||
)?;
|
||||
} else if fields.contains("api_formats") {
|
||||
updated.auth_type_by_format = normalize_auth_type_by_format(
|
||||
updated.auth_type_by_format.clone(),
|
||||
"auth_type_by_format",
|
||||
&effective_api_formats,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
updated.auth_type_by_format = None;
|
||||
}
|
||||
|
||||
updated.auth_type = target_auth_type;
|
||||
|
||||
if let Some(name) = payload.name {
|
||||
@@ -285,7 +296,7 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
if fields.contains("fingerprint") {
|
||||
updated.fingerprint = normalize_json_object(payload.fingerprint, "fingerprint")?;
|
||||
}
|
||||
if auth_config_present && !auth_type_switch && updated.auth_type != "api_key" {
|
||||
if auth_config_present && !auth_type_switch && !raw_secret_auth_type(&updated.auth_type) {
|
||||
updated.encrypted_auth_config = auth_config
|
||||
.as_ref()
|
||||
.map(serde_json::to_string)
|
||||
@@ -304,3 +315,10 @@ pub(crate) async fn build_admin_update_provider_key_record(
|
||||
.map(|duration| duration.as_secs());
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
fn raw_secret_auth_type(value: &str) -> bool {
|
||||
matches!(
|
||||
value.trim().to_ascii_lowercase().as_str(),
|
||||
"api_key" | "bearer"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,45 @@ pub(crate) fn normalize_api_format_json_object_keys(
|
||||
Ok(Some(serde_json::Value::Object(normalized)))
|
||||
}
|
||||
|
||||
pub(crate) fn normalize_auth_type_by_format(
|
||||
value: Option<serde_json::Value>,
|
||||
field_name: &str,
|
||||
api_formats: &[String],
|
||||
) -> Result<Option<serde_json::Value>, String> {
|
||||
let Some(value) = normalize_json_like_object(value, field_name)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let serde_json::Value::Object(map) = value else {
|
||||
return Ok(Some(value));
|
||||
};
|
||||
let allowed = api_formats.iter().cloned().collect::<BTreeSet<_>>();
|
||||
let mut normalized = serde_json::Map::new();
|
||||
for (key, value) in map {
|
||||
let canonical = crate::ai_pipeline::normalize_api_format_alias(&key);
|
||||
if !allowed.is_empty() && !allowed.contains(&canonical) {
|
||||
return Err(format!("{field_name} 包含未选择的 API 格式: {canonical}"));
|
||||
}
|
||||
let Some(auth_type) = value
|
||||
.as_str()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return Err(format!("{field_name}.{canonical} 必须是字符串"));
|
||||
};
|
||||
let auth_type = match auth_type.to_ascii_lowercase().as_str() {
|
||||
"api_key" | "apikey" | "api-key" => "api_key",
|
||||
"bearer" | "bearer_token" | "bearer-token" | "authorization" => "bearer",
|
||||
_ => return Err(format!("{field_name}.{canonical} 仅支持 api_key / bearer")),
|
||||
};
|
||||
normalized.insert(canonical, serde_json::Value::String(auth_type.to_string()));
|
||||
}
|
||||
if normalized.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(serde_json::Value::Object(normalized)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalize_auth_type(value: Option<&str>) -> Result<String, String> {
|
||||
let auth_type = value.unwrap_or("api_key").trim().to_ascii_lowercase();
|
||||
match auth_type.as_str() {
|
||||
@@ -111,7 +150,7 @@ fn normalize_json_like_object(
|
||||
mod tests {
|
||||
use super::{
|
||||
normalize_api_format_json_object_keys, normalize_api_format_list, normalize_auth_type,
|
||||
normalize_pool_advanced_config, validate_vertex_api_formats,
|
||||
normalize_auth_type_by_format, normalize_pool_advanced_config, validate_vertex_api_formats,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -180,6 +219,28 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_auth_type_by_format_accepts_per_format_bearer_override() {
|
||||
assert_eq!(
|
||||
normalize_auth_type_by_format(
|
||||
Some(json!({
|
||||
"claude:messages": "bearer",
|
||||
"gemini:generate_content": "api-key"
|
||||
})),
|
||||
"auth_type_by_format",
|
||||
&[
|
||||
"claude:messages".to_string(),
|
||||
"gemini:generate_content".to_string(),
|
||||
],
|
||||
)
|
||||
.expect("auth map should normalize"),
|
||||
Some(json!({
|
||||
"claude:messages": "bearer",
|
||||
"gemini:generate_content": "api_key"
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_vertex_api_formats_uses_canonical_message_formats() {
|
||||
assert!(validate_vertex_api_formats(
|
||||
|
||||
@@ -2,6 +2,7 @@ fn normalize_reveal_auth_type(value: &str) -> &str {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"service_account" | "vertex_ai" => "service_account",
|
||||
"oauth" => "oauth",
|
||||
"bearer" => "bearer",
|
||||
_ => "api_key",
|
||||
}
|
||||
}
|
||||
@@ -44,8 +45,10 @@ pub(crate) fn build_admin_reveal_key_payload(
|
||||
"auth_config": auth_config,
|
||||
}));
|
||||
}
|
||||
let decrypted = state
|
||||
.decrypt_catalog_secret_with_fallbacks(&key.encrypted_api_key)
|
||||
let decrypted = key
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.and_then(|ciphertext| state.decrypt_catalog_secret_with_fallbacks(ciphertext))
|
||||
.ok_or_else(|| {
|
||||
"无法解密认证配置,可能是加密密钥已更改。请重新添加该密钥。".to_string()
|
||||
})?;
|
||||
@@ -58,9 +61,14 @@ pub(crate) fn build_admin_reveal_key_payload(
|
||||
}));
|
||||
}
|
||||
|
||||
let decrypted = state
|
||||
.decrypt_catalog_secret_with_fallbacks(&key.encrypted_api_key)
|
||||
.ok_or_else(|| "无法解密 API Key,可能是加密密钥已更改。请重新添加该密钥。".to_string())?;
|
||||
let decrypted = match key.encrypted_api_key.as_deref().map(str::trim) {
|
||||
Some(ciphertext) if !ciphertext.is_empty() => state
|
||||
.decrypt_catalog_secret_with_fallbacks(ciphertext)
|
||||
.ok_or_else(|| {
|
||||
"无法解密 API Key,可能是加密密钥已更改。请重新添加该密钥。".to_string()
|
||||
})?,
|
||||
_ => String::new(),
|
||||
};
|
||||
Ok(json!({
|
||||
"auth_type": auth_type,
|
||||
"api_key": decrypted,
|
||||
|
||||
@@ -250,20 +250,18 @@ fn apply_imported_oauth_key_credentials(
|
||||
record: &mut aether_data_contracts::repository::provider_catalog::StoredProviderCatalogKey,
|
||||
) -> Result<(), String> {
|
||||
if let Some(api_key_value) = raw_key.get("api_key") {
|
||||
let plaintext = match api_key_value {
|
||||
Value::String(raw) => {
|
||||
let trimmed = raw.trim();
|
||||
if trimmed.is_empty() {
|
||||
"__placeholder__"
|
||||
} else {
|
||||
trimmed
|
||||
}
|
||||
}
|
||||
_ => "__placeholder__",
|
||||
let plaintext = api_key_value
|
||||
.as_str()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
record.encrypted_api_key = match plaintext {
|
||||
Some(plaintext) => Some(
|
||||
state
|
||||
.encrypt_catalog_secret_with_fallbacks(plaintext)
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
record.encrypted_api_key = state
|
||||
.encrypt_catalog_secret_with_fallbacks(plaintext)
|
||||
.ok_or_else(|| "gateway 未配置 provider key 加密密钥".to_string())?;
|
||||
}
|
||||
|
||||
if raw_key.contains_key("auth_config") {
|
||||
@@ -1118,14 +1116,15 @@ impl<'a> AdminAppState<'a> {
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned);
|
||||
existing_keys.iter().position(|existing_key| {
|
||||
let decrypted_existing = existing_key
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.and_then(|ciphertext| {
|
||||
self.decrypt_catalog_secret_with_fallbacks(ciphertext)
|
||||
});
|
||||
target_key
|
||||
.as_deref()
|
||||
.zip(
|
||||
self.decrypt_catalog_secret_with_fallbacks(
|
||||
&existing_key.encrypted_api_key,
|
||||
)
|
||||
.as_deref(),
|
||||
)
|
||||
.zip(decrypted_existing.as_deref())
|
||||
.is_some_and(|(target, decrypted)| decrypted == target)
|
||||
})
|
||||
} else if matches!(auth_type.as_str(), "service_account" | "vertex_ai") {
|
||||
|
||||
@@ -106,10 +106,10 @@ pub(crate) async fn build_admin_system_export_providers_payload(
|
||||
})
|
||||
.map(serde_json::Value::String);
|
||||
AdminSystemConfigProviderKey {
|
||||
api_key: Some(
|
||||
decrypt_admin_system_export_secret(state, &key.encrypted_api_key)
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
api_key: key.encrypted_api_key.as_deref().map(|ciphertext| {
|
||||
decrypt_admin_system_export_secret(state, ciphertext)
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
auth_type: Some(key.auth_type.clone()),
|
||||
auth_config,
|
||||
name: Some(key.name.clone()),
|
||||
@@ -119,6 +119,7 @@ pub(crate) async fn build_admin_system_export_providers_payload(
|
||||
rate_multipliers: key.rate_multipliers.clone(),
|
||||
internal_priority: Some(key.internal_priority),
|
||||
global_priority_by_format: key.global_priority_by_format.clone(),
|
||||
auth_type_by_format: key.auth_type_by_format.clone(),
|
||||
rpm_limit: key.rpm_limit,
|
||||
allowed_models: key.allowed_models.as_ref().and_then(|value| {
|
||||
value.as_array().map(|items| {
|
||||
|
||||
@@ -106,19 +106,29 @@ pub(crate) fn masked_catalog_api_key(state: &AppState, key: &StoredProviderCatal
|
||||
match key.auth_type.trim() {
|
||||
"service_account" | "vertex_ai" => "[Service Account]".to_string(),
|
||||
"oauth" => "[OAuth Token]".to_string(),
|
||||
_ => decrypt_catalog_secret_with_fallbacks(state.encryption_key(), &key.encrypted_api_key)
|
||||
.map(|value| {
|
||||
if value.len() <= 12 {
|
||||
format!("{value}***")
|
||||
} else {
|
||||
format!(
|
||||
"{}***{}",
|
||||
&value[..8],
|
||||
&value[value.len().saturating_sub(4)..]
|
||||
)
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "***ERROR***".to_string()),
|
||||
_ => {
|
||||
let Some(ciphertext) = key
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return "[未设置]".to_string();
|
||||
};
|
||||
decrypt_catalog_secret_with_fallbacks(state.encryption_key(), ciphertext)
|
||||
.map(|value| {
|
||||
if value.len() <= 12 {
|
||||
format!("{value}***")
|
||||
} else {
|
||||
format!(
|
||||
"{}***{}",
|
||||
&value[..8],
|
||||
&value[value.len().saturating_sub(4)..]
|
||||
)
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "***ERROR***".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,6 +1277,10 @@ pub(crate) fn build_admin_provider_key_response(
|
||||
);
|
||||
payload.insert("api_key_plain".to_string(), serde_json::Value::Null);
|
||||
payload.insert("auth_type".to_string(), json!(key.auth_type));
|
||||
payload.insert(
|
||||
"auth_type_by_format".to_string(),
|
||||
json!(key.auth_type_by_format),
|
||||
);
|
||||
payload.insert(
|
||||
"credential_kind".to_string(),
|
||||
json!(auth_semantics.credential_kind().as_str()),
|
||||
|
||||
@@ -759,6 +759,8 @@ mod tests {
|
||||
auth_type: auth_type.to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec![api_format.to_string()]),
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -172,6 +172,8 @@ mod tests {
|
||||
auth_type: "bearer".to_string(),
|
||||
is_active: true,
|
||||
api_formats: None,
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -304,6 +304,8 @@ mod tests {
|
||||
auth_type: "bearer".to_string(),
|
||||
is_active: true,
|
||||
api_formats: None,
|
||||
auth_type_by_format: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -26,6 +26,7 @@ pub(crate) enum ProviderKeyRuntimeAuthKind {
|
||||
ApiKey,
|
||||
Bearer,
|
||||
ServiceAccount,
|
||||
Mixed,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -35,6 +36,7 @@ impl ProviderKeyRuntimeAuthKind {
|
||||
Self::ApiKey => "api_key",
|
||||
Self::Bearer => "bearer",
|
||||
Self::ServiceAccount => "service_account",
|
||||
Self::Mixed => "mixed",
|
||||
Self::Unknown => "unknown",
|
||||
}
|
||||
}
|
||||
@@ -88,6 +90,13 @@ fn key_has_auth_config(key: &StoredProviderCatalogKey) -> bool {
|
||||
.is_some_and(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
fn key_has_auth_type_overrides(key: &StoredProviderCatalogKey) -> bool {
|
||||
key.auth_type_by_format
|
||||
.as_ref()
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.is_some_and(|items| !items.is_empty())
|
||||
}
|
||||
|
||||
fn provider_uses_bearer_oauth_runtime(provider_type: &str) -> bool {
|
||||
matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
@@ -129,11 +138,17 @@ pub(crate) fn provider_key_auth_semantics(
|
||||
}
|
||||
}
|
||||
ProviderKeyCredentialKind::ServiceAccount => ProviderKeyRuntimeAuthKind::ServiceAccount,
|
||||
ProviderKeyCredentialKind::RawSecret => match auth_type.as_str() {
|
||||
"bearer" => ProviderKeyRuntimeAuthKind::Bearer,
|
||||
"api_key" => ProviderKeyRuntimeAuthKind::ApiKey,
|
||||
_ => ProviderKeyRuntimeAuthKind::Unknown,
|
||||
},
|
||||
ProviderKeyCredentialKind::RawSecret => {
|
||||
if key_has_auth_type_overrides(key) {
|
||||
ProviderKeyRuntimeAuthKind::Mixed
|
||||
} else {
|
||||
match auth_type.as_str() {
|
||||
"bearer" => ProviderKeyRuntimeAuthKind::Bearer,
|
||||
"api_key" => ProviderKeyRuntimeAuthKind::ApiKey,
|
||||
_ => ProviderKeyRuntimeAuthKind::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ProviderKeyAuthSemantics {
|
||||
|
||||
@@ -1124,7 +1124,7 @@ impl AppState {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
latest_key.encrypted_api_key = encrypted_api_key;
|
||||
latest_key.encrypted_api_key = Some(encrypted_api_key);
|
||||
latest_key.encrypted_auth_config = encrypted_auth_config;
|
||||
latest_key.is_active = true;
|
||||
latest_key.expires_at_unix_secs = entry.expires_at_unix_secs;
|
||||
|
||||
@@ -339,9 +339,10 @@ async fn gateway_executes_openai_chat_stream_via_local_decision_gate_without_exe
|
||||
backup_key.id = "key-openai-local-stream-2".to_string();
|
||||
backup_key.provider_id = "provider-openai-local-stream-2".to_string();
|
||||
backup_key.name = "backup".to_string();
|
||||
backup_key.encrypted_api_key =
|
||||
backup_key.encrypted_api_key = Some(
|
||||
encrypt_python_fernet_plaintext(DEVELOPMENT_ENCRYPTION_KEY, "sk-upstream-openai-backup")
|
||||
.expect("api key should encrypt");
|
||||
.expect("api key should encrypt"),
|
||||
);
|
||||
let (upstream_url, upstream_handle) = start_server(upstream).await;
|
||||
let (provider_url, provider_handle) = start_server(provider).await;
|
||||
let mut primary_endpoint = sample_provider_catalog_endpoint();
|
||||
|
||||
@@ -311,9 +311,10 @@ async fn gateway_skips_unsupported_local_openai_chat_sync_candidate_before_tryin
|
||||
supported_key.id = "key-openai-skip-local-2".to_string();
|
||||
supported_key.provider_id = "provider-openai-skip-local-2".to_string();
|
||||
supported_key.name = "backup".to_string();
|
||||
supported_key.encrypted_api_key =
|
||||
supported_key.encrypted_api_key = Some(
|
||||
encrypt_python_fernet_plaintext(DEVELOPMENT_ENCRYPTION_KEY, "sk-upstream-openai-backup")
|
||||
.expect("api key should encrypt");
|
||||
.expect("api key should encrypt"),
|
||||
);
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![unsupported_provider, supported_provider],
|
||||
vec![unsupported_endpoint, supported_endpoint],
|
||||
|
||||
@@ -293,9 +293,10 @@ async fn gateway_executes_openai_chat_sync_via_local_decision_gate_without_execu
|
||||
backup_key.id = "key-openai-local-2".to_string();
|
||||
backup_key.provider_id = "provider-openai-local-2".to_string();
|
||||
backup_key.name = "backup".to_string();
|
||||
backup_key.encrypted_api_key =
|
||||
backup_key.encrypted_api_key = Some(
|
||||
encrypt_python_fernet_plaintext(DEVELOPMENT_ENCRYPTION_KEY, "sk-upstream-openai-backup")
|
||||
.expect("api key should encrypt");
|
||||
.expect("api key should encrypt"),
|
||||
);
|
||||
let (upstream_url, upstream_handle) = start_server(upstream).await;
|
||||
let (provider_url, provider_handle) = start_server(provider).await;
|
||||
let mut primary_endpoint = sample_provider_catalog_endpoint();
|
||||
|
||||
@@ -2848,7 +2848,9 @@ fn retired_api_format_occurrences_are_whitelisted() {
|
||||
"crates/aether-ai-formats/src/matrix.rs",
|
||||
"crates/aether-ai-formats/src/registry.rs",
|
||||
"crates/aether-ai-pipeline/src/conversion/registry.rs",
|
||||
"crates/aether-data/src/migrate.rs",
|
||||
"crates/aether-usage-runtime/src/report.rs",
|
||||
"frontend/src/api/endpoints/types/__tests__/api-format.spec.ts",
|
||||
];
|
||||
let allowed = allowed_paths
|
||||
.into_iter()
|
||||
|
||||
@@ -799,7 +799,10 @@ async fn gateway_updates_admin_provider_key_locally_with_trusted_admin_principal
|
||||
assert!(!reloaded[0].is_active);
|
||||
let decrypted = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
&reloaded[0].encrypted_api_key,
|
||||
reloaded[0]
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("ciphertext should decrypt");
|
||||
assert_eq!(decrypted, "sk-updated-openai");
|
||||
|
||||
@@ -421,9 +421,14 @@ async fn gateway_handles_admin_provider_oauth_device_poll_locally_with_trusted_a
|
||||
persisted.proxy,
|
||||
Some(json!({"node_id": "proxy-node-kiro", "enabled": true}))
|
||||
);
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, expected_access_token);
|
||||
let decrypted_auth_config = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
@@ -729,9 +734,14 @@ async fn gateway_revalidates_kiro_device_poll_via_idc_refresh_and_backfills_emai
|
||||
persisted.proxy,
|
||||
Some(json!({"node_id": "proxy-node-kiro", "enabled": true}))
|
||||
);
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, expected_refreshed_access_token);
|
||||
let decrypted_auth_config = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
@@ -1473,9 +1483,14 @@ async fn gateway_batch_imports_admin_provider_oauth_locally_with_trusted_admin_p
|
||||
persisted.proxy,
|
||||
Some(json!({"node_id": "proxy-node-batch-import", "enabled": true}))
|
||||
);
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, "batch-imported-codex-access-token");
|
||||
|
||||
gateway_handle.abort();
|
||||
@@ -1919,9 +1934,14 @@ async fn gateway_completes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
||||
.await
|
||||
.expect("keys should load");
|
||||
let persisted = reloaded.first().expect("persisted key should exist");
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, "new-codex-access-token");
|
||||
let decrypted_auth_config = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
@@ -2104,9 +2124,14 @@ async fn gateway_completes_admin_provider_oauth_provider_locally_with_trusted_ad
|
||||
persisted.proxy,
|
||||
Some(json!({"node_id": "proxy-node-codex-oauth", "enabled": true}))
|
||||
);
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, "provider-codex-access-token");
|
||||
let decrypted_auth_config = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
@@ -2275,9 +2300,14 @@ async fn gateway_imports_admin_provider_oauth_refresh_token_locally_with_trusted
|
||||
persisted.proxy,
|
||||
Some(json!({"node_id": "proxy-node-codex-import", "enabled": true}))
|
||||
);
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, "imported-codex-access-token");
|
||||
let decrypted_auth_config = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
@@ -2452,9 +2482,14 @@ async fn gateway_imports_admin_provider_oauth_refresh_token_over_active_expired_
|
||||
);
|
||||
assert_eq!(persisted.oauth_invalid_at_unix_secs, None);
|
||||
assert_eq!(persisted.oauth_invalid_reason, None);
|
||||
let decrypted_api_key =
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &persisted.encrypted_api_key)
|
||||
.expect("api key should decrypt");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
persisted
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, "imported-expired-codex-access-token");
|
||||
let decrypted_auth_config = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
@@ -4105,7 +4140,10 @@ async fn gateway_refreshes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
||||
.expect("refreshed key should exist");
|
||||
let decrypted_api_key = decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
stored_key.encrypted_api_key.as_str(),
|
||||
stored_key
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("refreshed api key should decrypt");
|
||||
assert_eq!(decrypted_api_key, "refreshed-codex-access-token");
|
||||
|
||||
@@ -348,8 +348,14 @@ async fn gateway_imports_admin_system_config_locally_and_persists_data() {
|
||||
.expect("keys should load");
|
||||
assert_eq!(keys.len(), 1);
|
||||
assert_eq!(
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &keys[0].encrypted_api_key)
|
||||
.expect("api key should decrypt"),
|
||||
decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
keys[0]
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("api key should decrypt"),
|
||||
"sk-import-123"
|
||||
);
|
||||
|
||||
@@ -1018,8 +1024,14 @@ async fn gateway_imports_oauth_provider_key_credentials_from_admin_system_config
|
||||
assert_eq!(keys.len(), 1);
|
||||
assert_eq!(keys[0].auth_type, "oauth");
|
||||
assert_eq!(
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &keys[0].encrypted_api_key)
|
||||
.expect("oauth access token should decrypt"),
|
||||
decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
keys[0]
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("oauth access token should decrypt"),
|
||||
"oauth-access-token-1"
|
||||
);
|
||||
let auth_config = decrypt_python_fernet_ciphertext(
|
||||
@@ -1108,8 +1120,14 @@ async fn gateway_overwrites_oauth_provider_key_credentials_from_admin_system_imp
|
||||
assert_eq!(keys.len(), 1);
|
||||
assert_eq!(keys[0].name, "oauth-primary");
|
||||
assert_eq!(
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &keys[0].encrypted_api_key)
|
||||
.expect("oauth access token should decrypt"),
|
||||
decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
keys[0]
|
||||
.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("oauth access token should decrypt"),
|
||||
"oauth-access-token-new"
|
||||
);
|
||||
let auth_config = decrypt_python_fernet_ciphertext(
|
||||
@@ -1282,8 +1300,13 @@ async fn gateway_overwrites_oauth_provider_key_credentials_from_admin_system_imp
|
||||
assert_eq!(key.oauth_invalid_reason, None);
|
||||
assert!(key.expires_at_unix_secs.is_some());
|
||||
assert_eq!(
|
||||
decrypt_python_fernet_ciphertext(DEVELOPMENT_ENCRYPTION_KEY, &key.encrypted_api_key)
|
||||
.expect("oauth access token should decrypt"),
|
||||
decrypt_python_fernet_ciphertext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
key.encrypted_api_key
|
||||
.as_deref()
|
||||
.expect("api key should be present"),
|
||||
)
|
||||
.expect("oauth access token should decrypt"),
|
||||
"oauth-access-token-refreshed"
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user