mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +08:00
fix(gateway): allow clearing API key IP whitelists
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use super::ADMIN_USERS_DATA_UNAVAILABLE_DETAIL;
|
use super::ADMIN_USERS_DATA_UNAVAILABLE_DETAIL;
|
||||||
use crate::handlers::admin::shared::AdminTypedObjectPatch;
|
use crate::handlers::admin::shared::AdminTypedObjectPatch;
|
||||||
|
use crate::handlers::shared::deserialize_optional_string_list_patch;
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
http,
|
http,
|
||||||
@@ -51,7 +52,7 @@ pub(super) struct AdminUpdateUserApiKeyRequest {
|
|||||||
pub(super) concurrent_limit: Option<i32>,
|
pub(super) concurrent_limit: Option<i32>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub(super) feature_settings: Option<Option<Value>>,
|
pub(super) feature_settings: Option<Option<Value>>,
|
||||||
#[serde(default)]
|
#[serde(default, deserialize_with = "deserialize_optional_string_list_patch")]
|
||||||
pub(super) allowed_ips: Option<Option<Vec<String>>>,
|
pub(super) allowed_ips: Option<Option<Vec<String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +389,8 @@ pub(super) fn format_optional_datetime_iso8601(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::normalize_admin_user_api_formats;
|
use super::{normalize_admin_user_api_formats, AdminUpdateUserApiKeyRequest};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn admin_user_api_formats_accept_current_canonical_signatures() {
|
fn admin_user_api_formats_accept_current_canonical_signatures() {
|
||||||
@@ -425,4 +427,31 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn admin_update_api_key_distinguishes_missing_null_and_present_allowed_ips() {
|
||||||
|
let missing = serde_json::from_value::<AdminUpdateUserApiKeyRequest>(json!({
|
||||||
|
"name": "unchanged-whitelist",
|
||||||
|
}))
|
||||||
|
.expect("missing allowed_ips should deserialize");
|
||||||
|
assert_eq!(missing.allowed_ips, None);
|
||||||
|
|
||||||
|
let cleared = serde_json::from_value::<AdminUpdateUserApiKeyRequest>(json!({
|
||||||
|
"allowed_ips": null,
|
||||||
|
}))
|
||||||
|
.expect("null allowed_ips should deserialize");
|
||||||
|
assert_eq!(cleared.allowed_ips, Some(None));
|
||||||
|
|
||||||
|
let updated = serde_json::from_value::<AdminUpdateUserApiKeyRequest>(json!({
|
||||||
|
"allowed_ips": ["203.0.113.10", "10.0.0.0/24"],
|
||||||
|
}))
|
||||||
|
.expect("present allowed_ips should deserialize");
|
||||||
|
assert_eq!(
|
||||||
|
updated.allowed_ips,
|
||||||
|
Some(Some(vec![
|
||||||
|
"203.0.113.10".to_string(),
|
||||||
|
"10.0.0.0/24".to_string(),
|
||||||
|
])),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ use serde_json::json;
|
|||||||
|
|
||||||
use crate::handlers::shared::{
|
use crate::handlers::shared::{
|
||||||
api_key_placeholder_display, deserialize_optional_json_patch,
|
api_key_placeholder_display, deserialize_optional_json_patch,
|
||||||
generate_gateway_api_key_plaintext, masked_gateway_api_key_display, normalize_feature_settings,
|
deserialize_optional_string_list_patch, generate_gateway_api_key_plaintext,
|
||||||
|
masked_gateway_api_key_display, normalize_feature_settings,
|
||||||
normalize_optional_api_key_concurrent_limit,
|
normalize_optional_api_key_concurrent_limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ struct UsersMeUpdateApiKeyRequest {
|
|||||||
concurrent_limit: Option<i32>,
|
concurrent_limit: Option<i32>,
|
||||||
#[serde(default, deserialize_with = "deserialize_optional_json_patch")]
|
#[serde(default, deserialize_with = "deserialize_optional_json_patch")]
|
||||||
feature_settings: Option<Option<serde_json::Value>>,
|
feature_settings: Option<Option<serde_json::Value>>,
|
||||||
#[serde(default)]
|
#[serde(default, deserialize_with = "deserialize_optional_string_list_patch")]
|
||||||
allowed_ips: Option<Option<Vec<String>>>,
|
allowed_ips: Option<Option<Vec<String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1130,7 +1131,8 @@ pub(super) async fn handle_users_me_api_key_capabilities_put(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::normalize_users_me_allowed_ips;
|
use super::{normalize_users_me_allowed_ips, UsersMeUpdateApiKeyRequest};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalize_allowed_ips_trims_ip_and_cidr_values() {
|
fn normalize_allowed_ips_trims_ip_and_cidr_values() {
|
||||||
@@ -1153,4 +1155,31 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(err, "无效的 IP 地址或 CIDR: 10.0.0.0/99");
|
assert_eq!(err, "无效的 IP 地址或 CIDR: 10.0.0.0/99");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_payload_distinguishes_missing_null_and_present_allowed_ips() {
|
||||||
|
let missing = serde_json::from_value::<UsersMeUpdateApiKeyRequest>(json!({
|
||||||
|
"name": "unchanged-whitelist",
|
||||||
|
}))
|
||||||
|
.expect("missing allowed_ips should deserialize");
|
||||||
|
assert_eq!(missing.allowed_ips, None);
|
||||||
|
|
||||||
|
let cleared = serde_json::from_value::<UsersMeUpdateApiKeyRequest>(json!({
|
||||||
|
"allowed_ips": null,
|
||||||
|
}))
|
||||||
|
.expect("null allowed_ips should deserialize");
|
||||||
|
assert_eq!(cleared.allowed_ips, Some(None));
|
||||||
|
|
||||||
|
let updated = serde_json::from_value::<UsersMeUpdateApiKeyRequest>(json!({
|
||||||
|
"allowed_ips": ["203.0.113.10", "10.0.0.0/24"],
|
||||||
|
}))
|
||||||
|
.expect("present allowed_ips should deserialize");
|
||||||
|
assert_eq!(
|
||||||
|
updated.allowed_ips,
|
||||||
|
Some(Some(vec![
|
||||||
|
"203.0.113.10".to_string(),
|
||||||
|
"10.0.0.0/24".to_string(),
|
||||||
|
])),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ pub(crate) use self::email_templates::{
|
|||||||
};
|
};
|
||||||
pub(crate) use self::external_models::OFFICIAL_EXTERNAL_MODEL_PROVIDERS;
|
pub(crate) use self::external_models::OFFICIAL_EXTERNAL_MODEL_PROVIDERS;
|
||||||
pub(crate) use self::normalize::{
|
pub(crate) use self::normalize::{
|
||||||
deserialize_optional_json_patch, normalize_feature_settings, normalize_json_array,
|
deserialize_optional_json_patch, deserialize_optional_string_list_patch,
|
||||||
normalize_json_object, normalize_string_list,
|
normalize_feature_settings, normalize_json_array, normalize_json_object, normalize_string_list,
|
||||||
};
|
};
|
||||||
pub(crate) use self::payloads::{
|
pub(crate) use self::payloads::{
|
||||||
InternalGatewayAuthContextRequest, InternalGatewayExecuteRequest,
|
InternalGatewayAuthContextRequest, InternalGatewayExecuteRequest,
|
||||||
|
|||||||
@@ -72,6 +72,15 @@ where
|
|||||||
<Option<Value> as serde::Deserialize>::deserialize(deserializer).map(Some)
|
<Option<Value> as serde::Deserialize>::deserialize(deserializer).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize_optional_string_list_patch<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<Option<Option<Vec<String>>>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
<Option<Vec<String>> as serde::Deserialize>::deserialize(deserializer).map(Some)
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_chat_pii_redaction_feature_settings(
|
fn normalize_chat_pii_redaction_feature_settings(
|
||||||
settings: &mut Map<String, Value>,
|
settings: &mut Map<String, Value>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
|||||||
Reference in New Issue
Block a user