feat: support api key ip restriction rules

This commit is contained in:
fawney19
2026-05-20 16:11:49 +08:00
parent b6bdc08267
commit f76bbaab52
55 changed files with 698 additions and 551 deletions
@@ -10,7 +10,8 @@ use crate::handlers::admin::users::{
default_admin_user_api_key_name, format_optional_unix_secs_iso8601,
generate_admin_user_api_key_plaintext, hash_admin_user_api_key, masked_user_api_key_display,
normalize_admin_feature_settings, normalize_admin_optional_api_key_name,
normalize_admin_user_api_formats, normalize_admin_user_string_list,
normalize_admin_user_api_formats, normalize_admin_user_ip_rules,
normalize_admin_user_string_list,
};
use crate::handlers::shared::normalize_optional_api_key_concurrent_limit;
use crate::GatewayError;
@@ -109,6 +110,10 @@ pub(super) async fn build_admin_create_api_key_response(
Ok(value) => value,
Err(detail) => return Ok(build_admin_api_keys_bad_request_response(detail)),
};
let ip_rules = match normalize_admin_user_ip_rules(payload.ip_rules) {
Ok(value) => value,
Err(detail) => return Ok(build_admin_api_keys_bad_request_response(detail)),
};
if payload.rate_limit.is_some_and(|value| value < 0) {
return Ok(build_admin_api_keys_bad_request_response(
"rate_limit 必须大于等于 0",
@@ -162,7 +167,7 @@ pub(super) async fn build_admin_create_api_key_response(
allowed_providers,
allowed_api_formats,
allowed_models,
allowed_ips: None,
ip_rules,
rate_limit: payload.rate_limit,
concurrent_limit,
force_capabilities: None,
@@ -328,6 +333,19 @@ pub(super) async fn build_admin_update_api_key_response(
} else {
None
};
let ip_rules_present =
field_presence.contains("ip_rules") || field_presence.contains("allowed_ips");
let ip_rules = if ip_rules_present {
match payload.ip_rules {
Some(value) => match normalize_admin_user_ip_rules(value) {
Ok(value) => Some(value),
Err(detail) => return Ok(build_admin_api_keys_bad_request_response(detail)),
},
None => Some(None),
}
} else {
None
};
let effective_expires_at_unix_secs = if field_presence.contains("expires_at") {
match parse_standalone_api_key_expires_at(payload.expires_at.as_deref()) {
Ok(value) => value,
@@ -395,7 +413,7 @@ pub(super) async fn build_admin_update_api_key_response(
allowed_providers,
allowed_api_formats,
allowed_models,
allowed_ips: None,
ip_rules,
expires_at_present: field_presence.contains("expires_at"),
expires_at_unix_secs: if field_presence.contains("expires_at") {
effective_expires_at_unix_secs
@@ -3,6 +3,7 @@ use crate::handlers::admin::shared::{query_param_value, AdminTypedObjectPatch};
use crate::handlers::admin::users::{
format_optional_unix_secs_iso8601, masked_user_api_key_display,
};
use crate::handlers::shared::deserialize_optional_string_list_patch;
use aether_admin::system::serialize_admin_system_users_export_wallet;
use axum::{
body::Body,
@@ -20,6 +21,8 @@ pub(super) struct AdminStandaloneApiKeyCreateRequest {
pub(super) allowed_providers: Option<Vec<String>>,
pub(super) allowed_api_formats: Option<Vec<String>>,
pub(super) allowed_models: Option<Vec<String>>,
#[serde(default, alias = "allowed_ips")]
pub(super) ip_rules: Option<Vec<String>>,
pub(super) rate_limit: Option<i32>,
pub(super) concurrent_limit: Option<i32>,
pub(super) initial_balance_usd: Option<f64>,
@@ -36,6 +39,12 @@ pub(super) struct AdminStandaloneApiKeyUpdateRequest {
pub(super) allowed_providers: Option<Vec<String>>,
pub(super) allowed_api_formats: Option<Vec<String>>,
pub(super) allowed_models: Option<Vec<String>>,
#[serde(
default,
alias = "allowed_ips",
deserialize_with = "deserialize_optional_string_list_patch"
)]
pub(super) ip_rules: Option<Option<Vec<String>>>,
pub(super) rate_limit: Option<i32>,
pub(super) concurrent_limit: Option<i32>,
pub(super) initial_balance_usd: Option<f64>,
@@ -161,6 +170,7 @@ pub(super) fn build_admin_api_key_list_item_payload(
"allowed_providers": record.allowed_providers,
"allowed_api_formats": record.allowed_api_formats,
"allowed_models": record.allowed_models,
"ip_rules": record.ip_rules,
"last_used_at": format_optional_unix_secs_iso8601(record.last_used_at_unix_secs),
"expires_at": format_optional_unix_secs_iso8601(record.expires_at_unix_secs),
"created_at": format_optional_unix_secs_iso8601(record.created_at_unix_secs),
@@ -191,6 +201,7 @@ pub(super) fn build_admin_api_key_detail_payload(
"allowed_providers": record.allowed_providers,
"allowed_api_formats": record.allowed_api_formats,
"allowed_models": record.allowed_models,
"ip_rules": record.ip_rules,
"last_used_at": format_optional_unix_secs_iso8601(record.last_used_at_unix_secs),
"expires_at": format_optional_unix_secs_iso8601(record.expires_at_unix_secs),
"created_at": format_optional_unix_secs_iso8601(record.created_at_unix_secs),