use serde::{Deserialize, Serialize}; use std::fmt; use crate::db::{Operation, Role}; /// A requested user/domain combo for login, lowercased. /// /// Domain may be empty, but a value with more than one /// `@` is considered invalid. #[derive(Clone, Debug)] pub struct UserRef { pub username: String, pub domain: Option, } #[derive(Clone, Debug)] pub struct InvalidUserRef(pub String); impl std::error::Error for InvalidUserRef {} impl fmt::Display for InvalidUserRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Invalid username: {}", self.0) } } impl UserRef { pub fn from_user_maybe_domain(value: &str) -> Result { let value = value.to_lowercase(); let mut parts = value.split('@'); let username = parts.next().unwrap(); let domain = parts.next(); if parts.next().is_some() { return Err(InvalidUserRef(value.clone())); } Ok(Self { username: username.to_string(), domain: domain.map(Into::into), }) } } impl fmt::Display for UserRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(domain) = &self.domain { write!(f, "{}@{}", self.username, domain) } else { write!(f, "{}", self.username) } } } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct User { /// Username, without the domain part. Once set, cannot be edited. pub username: String, /// Domain of the user. Once set, cannot be edited. /// /// Service accounts may exist with an empty domain. pub domain: Option, pub password: String, /// Mail for the user. Computed from username/domain, cannot be edited. pub mail: String, pub role: Role, // recovery_mail: String, } impl User { pub fn user_ref(&self) -> UserRef { UserRef { username: self.username.clone(), domain: self.domain.clone(), } } pub fn can_perform(&self, operation: &Operation) -> bool { self.role.can_perform(operation) } pub fn can_create_domain(&self) -> bool { self.role.can_perform(&Operation::CreateDomain) } pub fn can_see_domain(&self, domain: &str) -> bool { let allowed = self.role.can_see_domain(domain); tracing::debug!("{} can see domain {}: {}", self.mail, domain, allowed); allowed } } impl fmt::Display for User { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(domain) = &self.domain { write!(f, "{}@{}", self.username, domain) } else { write!(f, "{}", self.username) } } }