use crate::legion::{LegionError, LegionResult};
use phalanx_crypto::{Identity, PublicKey};
use std::collections::HashMap;
use tokio::sync::RwLock;
use serde::{Serialize, Deserialize};
use std::time::SystemTime;
#[derive(Debug)]
pub struct FederationManager {
federated_servers: RwLock<HashMap<String, FederatedServer>>,
bridge_configs: RwLock<HashMap<String, BridgeConfig>>,
policies: RwLock<FederationPolicy>,
}
#[derive(Debug, Clone)]
struct FederatedServer {
hostname: String,
public_key: PublicKey,
status: FederationStatus,
last_seen: SystemTime,
capabilities: Vec<String>,
trust_level: TrustLevel,
}
#[derive(Debug, Clone, PartialEq)]
enum FederationStatus {
Connected,
Connecting,
Disconnected,
Error(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum TrustLevel {
Trusted,
Limited,
Pending,
Blocked,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BridgeConfig {
pub name: String,
pub bridge_type: String,
pub endpoint: String,
pub credentials: BridgeCredentials,
pub channel_mappings: HashMap<String, String>,
pub features: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BridgeCredentials {
UserPass { username: String, password: String },
Token { token: String },
Certificate { cert_path: String, key_path: String },
LegionIdentity { identity_bytes: Vec<u8> },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct FederationPolicy {
enabled: bool,
auto_accept: bool,
max_servers: usize,
default_trust_level: TrustLevel,
allowed_bridge_types: Vec<String>,
security_requirements: SecurityRequirements,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SecurityRequirements {
require_tls: bool,
min_legion_version: String,
required_capabilities: Vec<String>,
cert_validation: CertValidation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
enum CertValidation {
Strict,
CustomCA { ca_path: String },
TOFU,
None,
}
impl Default for FederationPolicy {
fn default() -> Self {
Self {
enabled: false,
auto_accept: false,
max_servers: 10,
default_trust_level: TrustLevel::Pending,
allowed_bridge_types: vec![
"herald".to_string(),
"matrix".to_string(),
],
security_requirements: SecurityRequirements {
require_tls: true,
min_legion_version: "v1".to_string(),
required_capabilities: vec![
"legion-protocol/v1".to_string(),
"phalanx/v1".to_string(),
],
cert_validation: CertValidation::Strict,
},
}
}
}
impl FederationManager {
pub async fn new() -> LegionResult<Self> {
Ok(Self {
federated_servers: RwLock::new(HashMap::new()),
bridge_configs: RwLock::new(HashMap::new()),
policies: RwLock::new(FederationPolicy::default()),
})
}
pub async fn set_federation_enabled(&self, enabled: bool) -> LegionResult<()> {
let mut policies = self.policies.write().await;
policies.enabled = enabled;
tracing::info!("Federation {}", if enabled { "enabled" } else { "disabled" });
Ok(())
}
pub async fn is_federation_enabled(&self) -> bool {
let policies = self.policies.read().await;
policies.enabled
}
pub async fn add_federated_server(&self, hostname: String, public_key: PublicKey, trust_level: TrustLevel) -> LegionResult<()> {
if !self.is_federation_enabled().await {
return Err(LegionError::Federation("Federation is disabled".to_string()));
}
let policies = self.policies.read().await;
let servers = self.federated_servers.read().await;
if servers.len() >= policies.max_servers {
return Err(LegionError::Federation("Maximum federated servers reached".to_string()));
}
drop(servers);
drop(policies);
let server = FederatedServer {
hostname: hostname.clone(),
public_key,
status: FederationStatus::Connecting,
last_seen: SystemTime::now(),
capabilities: Vec::new(),
trust_level,
};
let mut servers = self.federated_servers.write().await;
servers.insert(hostname.clone(), server);
tracing::info!("Added federated server: {}", hostname);
Ok(())
}
pub async fn remove_federated_server(&self, hostname: &str) -> LegionResult<()> {
let mut servers = self.federated_servers.write().await;
if servers.remove(hostname).is_some() {
tracing::info!("Removed federated server: {}", hostname);
Ok(())
} else {
Err(LegionError::Federation(format!("Server not found: {}", hostname)))
}
}
pub async fn update_server_status(&self, hostname: &str, status: FederationStatus) -> LegionResult<()> {
let mut servers = self.federated_servers.write().await;
if let Some(server) = servers.get_mut(hostname) {
server.status = status;
server.last_seen = SystemTime::now();
Ok(())
} else {
Err(LegionError::Federation(format!("Server not found: {}", hostname)))
}
}
pub async fn get_server_info(&self, hostname: &str) -> LegionResult<FederatedServerInfo> {
let servers = self.federated_servers.read().await;
if let Some(server) = servers.get(hostname) {
Ok(FederatedServerInfo {
hostname: server.hostname.clone(),
status: format!("{:?}", server.status),
last_seen: server.last_seen,
capabilities: server.capabilities.clone(),
trust_level: format!("{:?}", server.trust_level),
})
} else {
Err(LegionError::Federation(format!("Server not found: {}", hostname)))
}
}
pub async fn list_federated_servers(&self) -> Vec<FederatedServerInfo> {
let servers = self.federated_servers.read().await;
servers.values().map(|server| FederatedServerInfo {
hostname: server.hostname.clone(),
status: format!("{:?}", server.status),
last_seen: server.last_seen,
capabilities: server.capabilities.clone(),
trust_level: format!("{:?}", server.trust_level),
}).collect()
}
pub async fn configure_bridge(&self, config: BridgeConfig) -> LegionResult<()> {
if !self.is_federation_enabled().await {
return Err(LegionError::Federation("Federation is disabled".to_string()));
}
let policies = self.policies.read().await;
if !policies.allowed_bridge_types.contains(&config.bridge_type) {
return Err(LegionError::Federation(format!("Bridge type not allowed: {}", config.bridge_type)));
}
drop(policies);
let mut bridges = self.bridge_configs.write().await;
bridges.insert(config.name.clone(), config.clone());
tracing::info!("Configured bridge: {} (type: {})", config.name, config.bridge_type);
Ok(())
}
pub async fn remove_bridge(&self, bridge_name: &str) -> LegionResult<()> {
let mut bridges = self.bridge_configs.write().await;
if bridges.remove(bridge_name).is_some() {
tracing::info!("Removed bridge: {}", bridge_name);
Ok(())
} else {
Err(LegionError::Federation(format!("Bridge not found: {}", bridge_name)))
}
}
pub async fn send_federated_message(&self, target_server: &str, channel: &str, message: Vec<u8>) -> LegionResult<()> {
let servers = self.federated_servers.read().await;
let server = servers.get(target_server)
.ok_or_else(|| LegionError::Federation(format!("Target server not found: {}", target_server)))?;
if server.trust_level == TrustLevel::Blocked {
return Err(LegionError::Federation("Target server is blocked".to_string()));
}
if server.status != FederationStatus::Connected {
return Err(LegionError::Federation("Target server is not connected".to_string()));
}
tracing::info!("Would send federated message to {} in channel {}", target_server, channel);
Ok(())
}
pub async fn process_federated_message(&self, source_server: &str, channel: &str, message: Vec<u8>) -> LegionResult<()> {
let servers = self.federated_servers.read().await;
let server = servers.get(source_server)
.ok_or_else(|| LegionError::Federation(format!("Source server not found: {}", source_server)))?;
if server.trust_level == TrustLevel::Blocked {
return Err(LegionError::Federation("Source server is blocked".to_string()));
}
tracing::info!("Processing federated message from {} in channel {}", source_server, channel);
Ok(())
}
pub async fn federation_stats(&self) -> FederationStats {
let servers = self.federated_servers.read().await;
let bridges = self.bridge_configs.read().await;
let policies = self.policies.read().await;
let connected_servers = servers.values()
.filter(|s| s.status == FederationStatus::Connected)
.count();
let trusted_servers = servers.values()
.filter(|s| s.trust_level == TrustLevel::Trusted)
.count();
FederationStats {
enabled: policies.enabled,
total_servers: servers.len(),
connected_servers,
trusted_servers,
total_bridges: bridges.len(),
active_bridges: 0, }
}
pub async fn cleanup(&self) -> LegionResult<()> {
let mut cleaned_servers = 0;
let now = SystemTime::now();
{
let mut servers = self.federated_servers.write().await;
let initial_count = servers.len();
servers.retain(|_, server| {
let inactive = now.duration_since(server.last_seen)
.unwrap_or(std::time::Duration::MAX)
.as_secs() < 24 * 60 * 60;
inactive || server.status == FederationStatus::Connected
});
cleaned_servers = initial_count - servers.len();
}
tracing::info!("Cleaned up {} inactive federated servers", cleaned_servers);
Ok(())
}
}
#[derive(Debug, Clone, Serialize)]
pub struct FederatedServerInfo {
pub hostname: String,
pub status: String,
pub last_seen: SystemTime,
pub capabilities: Vec<String>,
pub trust_level: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct FederationStats {
pub enabled: bool,
pub total_servers: usize,
pub connected_servers: usize,
pub trusted_servers: usize,
pub total_bridges: usize,
pub active_bridges: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use phalanx_crypto::Identity;
#[tokio::test]
async fn test_federation_manager_creation() {
let manager = FederationManager::new().await.unwrap();
assert!(!manager.is_federation_enabled().await);
assert_eq!(manager.list_federated_servers().await.len(), 0);
}
#[tokio::test]
async fn test_federation_enable_disable() {
let manager = FederationManager::new().await.unwrap();
manager.set_federation_enabled(true).await.unwrap();
assert!(manager.is_federation_enabled().await);
manager.set_federation_enabled(false).await.unwrap();
assert!(!manager.is_federation_enabled().await);
}
#[tokio::test]
async fn test_federated_server_management() {
let manager = FederationManager::new().await.unwrap();
manager.set_federation_enabled(true).await.unwrap();
let identity = Identity::generate();
let public_key = identity.public_key();
manager.add_federated_server(
"example.com".to_string(),
public_key,
TrustLevel::Trusted
).await.unwrap();
assert_eq!(manager.list_federated_servers().await.len(), 1);
let info = manager.get_server_info("example.com").await.unwrap();
assert_eq!(info.hostname, "example.com");
manager.remove_federated_server("example.com").await.unwrap();
assert_eq!(manager.list_federated_servers().await.len(), 0);
}
}