/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * * Modified by Coffey Labs in 2026 for INBUXA. */ use crate::{ expr::if_block::IfBlock, network::limiter::ConcurrencyLimiter, storage::{ObjectQuota, TenantQuota}, }; use directory::Credentials; use quick_cache::Equivalent; use registry::{ schema::enums::{Locale, Permission}, types::{EnumImpl, ipmask::IpAddrOrMask}, }; use std::{ hash::{Hash, Hasher}, net::IpAddr, sync::Arc, }; use tinyvec::TinyVec; use trc::ipc::bitset::Bitset; use types::collection::Collection; use utils::{cache::CacheItemWeight, map::bitmap::Bitmap}; pub mod access_token; pub mod authentication; pub mod credential; pub mod oauth; pub mod permissions; pub mod rate_limit; pub const RECOVERY_ADMIN_ID: u32 = u32::MAX; const PERMISSIONS_BITSET_SIZE: usize = Permission::COUNT.div_ceil(std::mem::size_of::()); pub type Permissions = Bitset; #[derive(Debug, PartialEq, Eq, Clone)] pub struct EmailAddress { pub local_part: Box, pub domain_id: u32, } #[derive(Debug, PartialEq, Eq)] pub struct EmailAddressRef<'x> { local_part: &'x str, domain_id: u32, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EmailCache { Account(u32), MailingList(u32), DisabledAccountAddress(u32), DisabledListAddress(u32), } #[derive(Debug, Clone)] pub struct DomainCache { pub names: Box<[Box]>, pub id: u32, pub id_directory: Option, pub id_tenant: Option, pub catch_all: Option>, pub sub_addressing_custom: Option>, pub flags: u8, } pub const DOMAIN_FLAG_RELAY: u8 = 1; pub const DOMAIN_FLAG_SUB_ADDRESSING: u8 = 1 << 1; // inbuxa: SCIM-15, SCIM-58 pub const DOMAIN_FLAG_SCIM: u8 = 1 << 2; #[derive(Debug, Clone, Default)] pub struct AccountCache { pub name: Box, pub id: u32, pub addresses: Box<[EmailAddress]>, pub id_tenant: Option, pub id_member_of: TinyVec<[u32; 3]>, pub quota_disk: u64, pub quota_objects: Option>, pub description: Option>, pub encryption_key: Option, pub locale: Locale, pub flags: u64, } pub type EncryptionKeys = Box<[Box<[u8]>]>; pub const ACCOUNT_IS_USER: u64 = 1; pub const ACCOUNT_FLAG_ENCRYPT_TRAIN_SPAM_FILTER: u64 = 1 << 1; pub const ACCOUNT_FLAG_ENCRYPT_METHOD_SMIME: u64 = 1 << 2; pub const ACCOUNT_FLAG_ENCRYPT_METHOD_PGP: u64 = 1 << 3; pub const ACCOUNT_FLAG_ENCRYPT_ALGO_AES256: u64 = 1 << 4; pub const ACCOUNT_FLAG_ENCRYPT_ALGO_AES128: u64 = 1 << 5; pub const ACCOUNT_FLAG_ENCRYPT_APPEND: u64 = 1 << 6; pub const ACCOUNT_FLAG_ENCRYPT_ALGO_AES256_GCM: u64 = 1 << 7; pub const ACCOUNT_FLAG_ENCRYPT_ALGO_CHACHA20_POLY1305: u64 = 1 << 8; #[derive(Debug, Clone)] pub struct RoleCache { pub id_roles: TinyVec<[u32; 3]>, pub permissions: PermissionsGroup, } #[derive(Debug, Clone)] pub struct MailingListCache { pub addresses: Box<[EmailAddress]>, pub recipients: Arc<[Box]>, } #[derive(Debug, Clone)] pub struct TenantCache { pub id_roles: TinyVec<[u32; 3]>, pub quota_disk: u64, pub quota_objects: Option>, pub permissions: Option>, } #[derive(Debug, Clone, Default)] pub struct PermissionsGroup { pub enabled: Permissions, pub disabled: Permissions, pub merge: bool, } #[derive(Debug, Default, Clone)] pub struct AccessToken { scope_idx: usize, inner: Arc, } #[derive(Debug, Default, Clone)] pub struct AccessTokenInner { pub(crate) account_id: u32, pub(crate) tenant_id: Option, pub(crate) member_of: TinyVec<[u32; 3]>, pub(crate) access_to: Box<[AccessTo]>, pub(crate) scopes: Box<[AccessScope]>, pub(crate) concurrent_http_requests: Option, pub(crate) concurrent_imap_requests: Option, pub(crate) concurrent_uploads: Option, pub(crate) revision_account: u64, pub(crate) revision: u64, pub(crate) credential_version: u64, pub(crate) obj_size: u64, } #[derive(Debug, Default, Hash, Clone)] pub struct AccessScope { pub permissions: Permissions, pub credential_id: u32, pub expires_at: u64, pub allowed_ips: Box<[IpAddrOrMask]>, } #[derive(Debug, Default, Hash, PartialEq, Eq, Clone)] pub(crate) struct AccessTo { pub account_id: u32, pub collections: Bitmap, } #[derive(Clone)] pub struct AccountInfo { pub account_id: u32, pub account: Arc, pub addresses: Vec, } #[derive(Clone, Copy)] pub struct AccountTenantIds { pub account_id: u32, pub tenant_id: Option, } pub struct AuthRequest { pub credentials: Credentials, pub session_id: u64, pub remote_ip: IpAddr, } impl CacheItemWeight for AccessTokenInner { fn weight(&self) -> u64 { self.obj_size } } impl CacheItemWeight for EmailAddress { fn weight(&self) -> u64 { std::mem::size_of::() as u64 + self.local_part.len() as u64 } } impl CacheItemWeight for EmailCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 } } impl CacheItemWeight for DomainCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 + self .names .iter() .map(|s| s.len() as u64 + std::mem::size_of::>() as u64) .sum::() + self.catch_all.as_ref().map_or(0, |s| s.len() as u64) + self .sub_addressing_custom .as_ref() .map_or(0, |s| s.weight()) } } impl Equivalent for EmailAddressRef<'_> { fn equivalent(&self, key: &EmailAddress) -> bool { self.local_part == &*key.local_part && self.domain_id == key.domain_id } } impl Hash for EmailAddress { fn hash(&self, state: &mut H) { self.local_part.as_ref().hash(state); self.domain_id.hash(state); } } impl Hash for EmailAddressRef<'_> { fn hash(&self, state: &mut H) { self.local_part.hash(state); self.domain_id.hash(state); } } impl CacheItemWeight for AccountCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 + self.name.len() as u64 + self .addresses .iter() .map(|s| s.local_part.len() as u64 + std::mem::size_of::() as u64) .sum::() + self.description.as_ref().map_or(0, |s| s.len() as u64) + self.encryption_key.as_ref().map_or(0, |keys| { keys.iter() .map(|k| k.len() as u64 + std::mem::size_of::>() as u64) .sum::() }) } } impl CacheItemWeight for RoleCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 } } impl CacheItemWeight for MailingListCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 + self .addresses .iter() .map(|s| s.local_part.len() as u64 + std::mem::size_of::() as u64) .sum::() + self .recipients .iter() .map(|s| s.len() as u64 + std::mem::size_of::>() as u64) .sum::() } } impl CacheItemWeight for TenantCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 + self.permissions.as_ref().map_or(0, |p| p.weight()) } } impl CacheItemWeight for PermissionsGroup { fn weight(&self) -> u64 { std::mem::size_of::() as u64 } } pub trait BuildAccessToken { fn build(self) -> AccessToken; } impl BuildAccessToken for Arc { fn build(self) -> AccessToken { AccessToken { scope_idx: 0, inner: self, } } } impl EmailAddress { pub fn new(local_part: impl Into>, domain_id: u32) -> Self { Self { local_part: local_part.into(), domain_id, } } } impl<'x> EmailAddressRef<'x> { pub fn new(local_part: &'x str, domain_id: u32) -> Self { Self { local_part, domain_id, } } } impl AccountCache { pub fn domain_id(&self) -> Option { self.addresses.first().map(|address| address.domain_id) } } impl DomainCache { pub fn name(&self) -> &str { self.names.first().map(|s| s.as_ref()).unwrap_or_default() } // inbuxa: SCIM-15, SCIM-58 pub fn allows_scim(&self) -> bool { self.flags & DOMAIN_FLAG_SCIM != 0 } }