Pumpkin/crates/pumpkin-config/src/networking/auth.rs
2026-08-09 17:06:40 +02:00

208 lines
6.9 KiB
Rust

use pumpkin_util::ProfileAction;
use serde::{Deserialize, Serialize};
/// Configuration for server authentication.
///
/// Handles Mojang authentication, proxy restrictions, player profiles, and textures.
#[derive(Deserialize, Serialize, Clone)]
#[serde(default)]
pub struct AuthenticationConfig {
/// Whether to use Mojang authentication.
pub enabled: bool,
/// Optional custom authentication URL.
pub url: Option<String>,
/// Fallback authentication server URLs to use if the primary/official server is down.
#[serde(alias = "fallback_urls")]
pub fallbacks: Vec<String>,
/// Optional custom profile lookup by username URL (template parameter `{username}`).
pub profile_by_name_url: Option<String>,
/// Optional fallback profile lookup by username URLs.
#[serde(alias = "profile_by_name_fallback_urls")]
pub profile_by_name_fallbacks: Vec<String>,
/// Optional custom profile lookup by UUID URL (template parameter `{uuid}`).
pub profile_by_uuid_url: Option<String>,
/// Optional fallback profile lookup by UUID URLs.
#[serde(alias = "profile_by_uuid_fallback_urls")]
pub profile_by_uuid_fallbacks: Vec<String>,
/// Connection timeout in milliseconds.
pub connect_timeout: u32,
/// Read timeout in milliseconds.
pub read_timeout: u32,
/// Whether to prevent connections via proxy.
pub prevent_proxy_connections: bool,
/// Optional auth URL used when preventing proxy connections.
pub prevent_proxy_connection_auth_url: Option<String>,
/// Public services URL (used by Drasl and Mojang).
pub services_url: Option<String>,
/// Player profile handling.
pub player_profile: PlayerProfileConfig,
/// Texture handling configuration.
pub textures: TextureConfig,
}
impl Default for AuthenticationConfig {
fn default() -> Self {
Self {
enabled: true,
prevent_proxy_connections: false,
player_profile: PlayerProfileConfig::default(),
textures: TextureConfig::default(),
url: None,
fallbacks: Vec::new(),
profile_by_name_url: None,
profile_by_name_fallbacks: Vec::new(),
profile_by_uuid_url: None,
profile_by_uuid_fallbacks: Vec::new(),
prevent_proxy_connection_auth_url: None,
services_url: None,
connect_timeout: 5000,
read_timeout: 5000,
}
}
}
/// Configuration for player profile handling.
///
/// Controls whether banned players are allowed and which profile actions are permitted.
#[derive(Deserialize, Serialize, Clone)]
#[serde(default)]
pub struct PlayerProfileConfig {
/// Allow players flagged by Mojang (e.g. banned, forced name change).
pub allow_banned_players: bool,
/// Depends on [`PlayerProfileConfig::allow_banned_players`].
pub allowed_actions: Vec<ProfileAction>,
}
impl Default for PlayerProfileConfig {
fn default() -> Self {
Self {
allow_banned_players: false,
allowed_actions: vec![
ProfileAction::ForcedNameChange,
ProfileAction::UsingBannedSkin,
],
}
}
}
/// Configuration for player textures.
///
/// Controls whether textures are applied, allowed URL schemes/domains, and texture types.
#[derive(Deserialize, Serialize, Clone)]
#[serde(default)]
pub struct TextureConfig {
/// Whether to use player textures.
pub enabled: bool,
/// Allowed URL schemes for texture URLs.
pub allowed_url_schemes: Vec<String>,
/// Allowed URL domains for texture URLs.
pub allowed_url_domains: Vec<String>,
/// Specific texture types.
pub types: TextureTypes,
}
impl Default for TextureConfig {
fn default() -> Self {
Self {
enabled: true,
allowed_url_schemes: vec!["http".into(), "https".into()],
allowed_url_domains: vec![".minecraft.net".into(), ".mojang.com".into()],
types: TextureTypes::default(),
}
}
}
/// Specifies which player texture types are supported.
#[derive(Deserialize, Serialize, Clone)]
#[serde(default)]
pub struct TextureTypes {
/// Use player skins.
pub skin: bool,
/// Use player capes.
pub cape: bool,
/// Use player elytras.
pub elytra: bool,
}
impl Default for TextureTypes {
fn default() -> Self {
Self {
skin: true,
cape: true,
elytra: true,
}
}
}
#[cfg(test)]
mod tests {
use super::AuthenticationConfig;
#[test]
fn auth_config_fallbacks_toml_deserialization() {
let toml_str = r#"
enabled = true
url = "https://primary.auth/hasJoined?username={username}&serverId={server_hash}"
fallbacks = [
"https://fallback1.auth/hasJoined?username={username}&serverId={server_hash}",
"https://fallback2.auth/hasJoined?username={username}&serverId={server_hash}"
]
"#;
let config: AuthenticationConfig =
toml::from_str(toml_str).expect("config should deserialize");
assert_eq!(
config.url.as_deref(),
Some("https://primary.auth/hasJoined?username={username}&serverId={server_hash}")
);
assert_eq!(config.fallbacks.len(), 2);
assert_eq!(
config.fallbacks[0],
"https://fallback1.auth/hasJoined?username={username}&serverId={server_hash}"
);
}
#[test]
fn auth_config_fallback_urls_alias_toml_deserialization() {
let toml_str = r#"
enabled = true
fallback_urls = [
"https://fallback1.auth/hasJoined?username={username}&serverId={server_hash}"
]
"#;
let config: AuthenticationConfig =
toml::from_str(toml_str).expect("config with alias should deserialize");
assert_eq!(config.fallbacks.len(), 1);
assert_eq!(
config.fallbacks[0],
"https://fallback1.auth/hasJoined?username={username}&serverId={server_hash}"
);
}
#[test]
fn auth_config_profile_urls_toml_deserialization() {
let toml_str = r#"
enabled = true
profile_by_name_url = "https://custom.auth/users/profiles/minecraft/{username}"
profile_by_name_fallback_urls = [
"https://fallback.auth/users/profiles/minecraft/{username}"
]
profile_by_uuid_url = "https://custom.auth/session/minecraft/profile/{uuid}?unsigned=false"
profile_by_uuid_fallback_urls = [
"https://fallback.auth/session/minecraft/profile/{uuid}?unsigned=false"
]
"#;
let config: AuthenticationConfig =
toml::from_str(toml_str).expect("profile config should deserialize");
assert_eq!(
config.profile_by_name_url.as_deref(),
Some("https://custom.auth/users/profiles/minecraft/{username}")
);
assert_eq!(config.profile_by_name_fallbacks.len(), 1);
assert_eq!(
config.profile_by_uuid_url.as_deref(),
Some("https://custom.auth/session/minecraft/profile/{uuid}?unsigned=false")
);
assert_eq!(config.profile_by_uuid_fallbacks.len(), 1);
}
}