use crate::error::Result;
use crate::email_server::smtp_server::AuthHandler;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use serde::{Deserialize, Serialize};
pub struct SynapseAuthHandler {
users: Arc<Mutex<HashMap<String, UserAccount>>>,
routing_permissions: Arc<Mutex<RoutingPermissions>>,
config: AuthConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserAccount {
pub username: String,
pub password_hash: String,
pub email: String,
pub permissions: UserPermissions,
pub active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPermissions {
pub can_send: bool,
pub can_receive: bool,
pub can_relay: bool,
pub is_admin: bool,
}
#[derive(Debug, Clone)]
pub struct RoutingPermissions {
pub local_domains: Vec<String>,
pub relay_domains: Vec<String>,
pub relay_ips: Vec<String>,
pub max_message_size: HashMap<String, usize>,
}
#[derive(Debug, Clone)]
pub struct AuthConfig {
pub require_auth_for_send: bool,
pub require_auth_for_receive: bool,
pub allow_anonymous_relay: bool,
pub default_permissions: UserPermissions,
}
impl Default for AuthConfig {
fn default() -> Self {
Self {
require_auth_for_send: true,
require_auth_for_receive: false, allow_anonymous_relay: false,
default_permissions: UserPermissions {
can_send: true,
can_receive: true,
can_relay: false,
is_admin: false,
},
}
}
}
impl Default for RoutingPermissions {
fn default() -> Self {
Self {
local_domains: vec!["localhost".to_string(), "127.0.0.1".to_string()],
relay_domains: Vec::new(),
relay_ips: vec!["127.0.0.1".to_string(), "::1".to_string()],
max_message_size: HashMap::new(),
}
}
}
impl SynapseAuthHandler {
pub fn new() -> Self {
let mut users = HashMap::new();
users.insert("admin".to_string(), UserAccount {
username: "admin".to_string(),
password_hash: "admin".to_string(), email: "admin@localhost".to_string(),
permissions: UserPermissions {
can_send: true,
can_receive: true,
can_relay: true,
is_admin: true,
},
active: true,
});
users.insert("emrp".to_string(), UserAccount {
username: "emrp".to_string(),
password_hash: "emrp123".to_string(), email: "emrp@localhost".to_string(),
permissions: UserPermissions {
can_send: true,
can_receive: true,
can_relay: false,
is_admin: false,
},
active: true,
});
Self {
users: Arc::new(Mutex::new(users)),
routing_permissions: Arc::new(Mutex::new(RoutingPermissions::default())),
config: AuthConfig::default(),
}
}
pub fn with_config(config: AuthConfig) -> Self {
let mut handler = Self::new();
handler.config = config;
handler
}
pub fn add_user(&self, user: UserAccount) -> Result<()> {
let mut users = self.users.lock().unwrap();
users.insert(user.username.clone(), user);
Ok(())
}
pub fn remove_user(&self, username: &str) -> Result<bool> {
let mut users = self.users.lock().unwrap();
Ok(users.remove(username).is_some())
}
pub fn update_routing_permissions(&self, permissions: RoutingPermissions) -> Result<()> {
let mut routing = self.routing_permissions.lock().unwrap();
*routing = permissions;
Ok(())
}
pub fn add_local_domain(&self, domain: &str) -> Result<()> {
let mut routing = self.routing_permissions.lock().unwrap();
if !routing.local_domains.contains(&domain.to_string()) {
routing.local_domains.push(domain.to_string());
}
Ok(())
}
pub fn add_relay_domain(&self, domain: &str) -> Result<()> {
let mut routing = self.routing_permissions.lock().unwrap();
if !routing.relay_domains.contains(&domain.to_string()) {
routing.relay_domains.push(domain.to_string());
}
Ok(())
}
fn is_local_domain(&self, email: &str) -> bool {
let routing = self.routing_permissions.lock().unwrap();
if let Some(domain) = email.split('@').nth(1) {
routing.local_domains.iter().any(|local_domain| {
domain == local_domain || domain.ends_with(&format!(".{}", local_domain))
})
} else {
false
}
}
fn is_relay_allowed(&self, email: &str) -> bool {
let routing = self.routing_permissions.lock().unwrap();
if let Some(domain) = email.split('@').nth(1) {
routing.relay_domains.iter().any(|relay_domain| {
domain == relay_domain || domain.ends_with(&format!(".{}", relay_domain))
})
} else {
false
}
}
fn get_user(&self, username: &str) -> Option<UserAccount> {
let users = self.users.lock().unwrap();
users.get(username).cloned()
}
fn get_user_by_email(&self, email: &str) -> Option<UserAccount> {
let users = self.users.lock().unwrap();
users.values().find(|user| user.email == email).cloned()
}
}
impl AuthHandler for SynapseAuthHandler {
fn authenticate(&self, username: &str, password: &str) -> Result<bool> {
if let Some(user) = self.get_user(username) {
if user.active && user.password_hash == password {
return Ok(true);
}
}
Ok(false)
}
fn is_authorized_sender(&self, email: &str) -> Result<bool> {
if !self.config.require_auth_for_send {
return Ok(self.is_local_domain(email));
}
if let Some(user) = self.get_user_by_email(email) {
Ok(user.active && user.permissions.can_send)
} else {
Ok(self.is_local_domain(email))
}
}
fn is_authorized_recipient(&self, email: &str) -> Result<bool> {
if self.is_local_domain(email) {
return Ok(true);
}
if self.is_relay_allowed(email) {
return Ok(true);
}
if let Some(user) = self.get_user_by_email(email) {
return Ok(user.active && user.permissions.can_relay);
}
Ok(false)
}
}
pub fn create_test_auth_handler() -> SynapseAuthHandler {
let handler = SynapseAuthHandler::new();
handler.add_local_domain("synapse.local").unwrap();
handler.add_local_domain("test.com").unwrap();
handler.add_relay_domain("example.com").unwrap();
handler.add_user(UserAccount {
username: "testuser".to_string(),
password_hash: "testpass".to_string(),
email: "test@synapse.local".to_string(),
permissions: UserPermissions {
can_send: true,
can_receive: true,
can_relay: true,
is_admin: false,
},
active: true,
}).unwrap();
handler
}