mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-09-08 13:04:26 +08:00
EE code reorganisation
This commit is contained in:
parent
38ff4b9ea0
commit
e683deb74e
19 changed files with 94 additions and 192 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -1054,7 +1054,6 @@ dependencies = [
|
|||
"rustls 0.23.10",
|
||||
"rustls-pemfile 2.1.2",
|
||||
"rustls-pki-types",
|
||||
"se_licensing",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
|
@ -3180,7 +3179,6 @@ dependencies = [
|
|||
"reqwest 0.12.5",
|
||||
"rev_lines",
|
||||
"rsa",
|
||||
"se_common",
|
||||
"sequoia-openpgp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -3589,7 +3587,6 @@ dependencies = [
|
|||
"jmap_proto",
|
||||
"managesieve",
|
||||
"pop3",
|
||||
"se_common",
|
||||
"smtp",
|
||||
"store",
|
||||
"tokio",
|
||||
|
@ -5627,25 +5624,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b84345e4c9bd703274a082fb80caaa99b7612be48dfaa1dd9266577ec412309d"
|
||||
|
||||
[[package]]
|
||||
name = "se_common"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"common",
|
||||
"serde",
|
||||
"store",
|
||||
"tracing",
|
||||
"utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "se_licensing"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ring 0.17.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
|
|
|
@ -12,8 +12,6 @@ members = [
|
|||
"crates/nlp",
|
||||
"crates/store",
|
||||
"crates/directory",
|
||||
"crates/se-licensing",
|
||||
"crates/se-common",
|
||||
"crates/utils",
|
||||
"crates/common",
|
||||
"crates/cli",
|
||||
|
|
|
@ -10,7 +10,6 @@ nlp = { path = "../nlp" }
|
|||
store = { path = "../store" }
|
||||
directory = { path = "../directory" }
|
||||
jmap_proto = { path = "../jmap-proto" }
|
||||
se_licensing = { path = "../se-licensing", optional = true }
|
||||
sieve-rs = { version = "0.5" }
|
||||
mail-parser = { version = "0.9", features = ["full_encoding", "ludicrous_mode"] }
|
||||
mail-auth = { version = "0.4" }
|
||||
|
@ -67,4 +66,4 @@ tracing-journald = "0.3"
|
|||
|
||||
[features]
|
||||
test_mode = []
|
||||
enterprise = ["se_licensing"]
|
||||
enterprise = []
|
||||
|
|
|
@ -16,13 +16,6 @@ use crate::{
|
|||
Network,
|
||||
};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::Enterprise;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use jmap_proto::types::collection::Collection;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use se_licensing::license::LicenseValidator;
|
||||
|
||||
use self::{
|
||||
imap::ImapConfig, jmap::settings::JmapConfig, scripts::Scripting, smtp::SmtpConfig,
|
||||
storage::Storage,
|
||||
|
@ -123,60 +116,6 @@ impl Core {
|
|||
.directories
|
||||
.insert("*".to_string(), directory.clone());
|
||||
|
||||
// SPDX-SnippetBegin
|
||||
// SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
|
||||
// SPDX-License-Identifier: LicenseRef-SEL
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
let enterprise = match config.value("enterprise.license-key").map(|key| {
|
||||
LicenseValidator::new().try_parse(key).and_then(|key| {
|
||||
key.into_validated_key(config.value("lookup.default.hostname").unwrap_or_default())
|
||||
})
|
||||
}) {
|
||||
Some(Ok(license)) => {
|
||||
match data
|
||||
.get_bitmap(store::BitmapKey::document_ids(
|
||||
u32::MAX,
|
||||
Collection::Principal,
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(Some(bitmap)) if bitmap.len() > license.accounts as u64 => {
|
||||
config.new_build_warning(
|
||||
"enterprise.license-key",
|
||||
format!(
|
||||
"License key is valid but only allows {} accounts, found {}.",
|
||||
license.accounts,
|
||||
bitmap.len()
|
||||
),
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(e) => {
|
||||
if !matches!(data, Store::None) {
|
||||
config.new_build_error("enterprise.license-key", e.to_string());
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => Some(Enterprise {
|
||||
license,
|
||||
undelete_period: config
|
||||
.property_or_default::<Option<std::time::Duration>>(
|
||||
"enterprise.undelete-period",
|
||||
"false",
|
||||
)
|
||||
.unwrap_or_default(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
config.new_build_warning("enterprise.license-key", e.to_string());
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
// SPDX-SnippetEnd
|
||||
|
||||
// If any of the stores are missing, disable all stores to avoid data loss
|
||||
if matches!(data, Store::None)
|
||||
|| matches!(&blob.backend, BlobBackend::Store(Store::None))
|
||||
|
@ -194,6 +133,8 @@ impl Core {
|
|||
}
|
||||
|
||||
Self {
|
||||
#[cfg(feature = "enterprise")]
|
||||
enterprise: crate::enterprise::Enterprise::parse(config, &data).await,
|
||||
sieve: Scripting::parse(config, &stores).await,
|
||||
network: Network::parse(config),
|
||||
smtp: SmtpConfig::parse(config).await,
|
||||
|
@ -215,8 +156,6 @@ impl Core {
|
|||
blobs: stores.blob_stores,
|
||||
ftss: stores.fts_stores,
|
||||
},
|
||||
#[cfg(feature = "enterprise")]
|
||||
enterprise,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
64
crates/common/src/enterprise/config.rs
Normal file
64
crates/common/src/enterprise/config.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: LicenseRef-SEL
|
||||
*
|
||||
* This file is subject to the Stalwart Enterprise License Agreement (SEL) and
|
||||
* is NOT open source software.
|
||||
*
|
||||
*/
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::{BitmapKey, Store};
|
||||
use utils::config::Config;
|
||||
|
||||
use super::{license::LicenseValidator, Enterprise};
|
||||
|
||||
impl Enterprise {
|
||||
pub async fn parse(config: &mut Config, data: &Store) -> Option<Self> {
|
||||
let license = match LicenseValidator::new()
|
||||
.try_parse(config.value("enterprise.license-key")?)
|
||||
.and_then(|key| {
|
||||
key.into_validated_key(config.value("lookup.default.hostname").unwrap_or_default())
|
||||
}) {
|
||||
Ok(key) => key,
|
||||
Err(err) => {
|
||||
config.new_build_warning("enterprise.license-key", err.to_string());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
match data
|
||||
.get_bitmap(BitmapKey::document_ids(u32::MAX, Collection::Principal))
|
||||
.await
|
||||
{
|
||||
Ok(Some(bitmap)) if bitmap.len() > license.accounts as u64 => {
|
||||
config.new_build_warning(
|
||||
"enterprise.license-key",
|
||||
format!(
|
||||
"License key is valid but only allows {} accounts, found {}.",
|
||||
license.accounts,
|
||||
bitmap.len()
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Err(e) => {
|
||||
if !matches!(data, Store::None) {
|
||||
config.new_build_error("enterprise.license-key", e.to_string());
|
||||
}
|
||||
return None;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Some(Enterprise {
|
||||
license,
|
||||
undelete_period: config
|
||||
.property_or_default::<Option<Duration>>("enterprise.undelete-period", "false")
|
||||
.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -8,18 +8,23 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub mod config;
|
||||
pub mod license;
|
||||
pub mod undelete;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub trait EnterpriseCore {
|
||||
fn is_enterprise_edition(&self) -> bool;
|
||||
fn log_license_details(&self);
|
||||
fn licensed_accounts(&self) -> u32;
|
||||
use std::time::Duration;
|
||||
|
||||
use license::LicenseKey;
|
||||
|
||||
use crate::Core;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Enterprise {
|
||||
pub license: LicenseKey,
|
||||
pub undelete_period: Option<Duration>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
impl EnterpriseCore for common::Core {
|
||||
impl Core {
|
||||
// WARNING: TAMPERING WITH THIS FUNCTION IS STRICTLY PROHIBITED
|
||||
// Any attempt to modify, bypass, or disable this license validation mechanism
|
||||
// constitutes a severe violation of the Stalwart Enterprise License Agreement.
|
||||
|
@ -29,17 +34,17 @@ impl EnterpriseCore for common::Core {
|
|||
// violators to the fullest extent of the law, including but not limited to claims
|
||||
// for copyright infringement, breach of contract, and fraud.
|
||||
|
||||
fn is_enterprise_edition(&self) -> bool {
|
||||
pub fn is_enterprise_edition(&self) -> bool {
|
||||
self.enterprise
|
||||
.as_ref()
|
||||
.map_or(false, |e| !e.license.is_expired())
|
||||
}
|
||||
|
||||
fn licensed_accounts(&self) -> u32 {
|
||||
pub fn licensed_accounts(&self) -> u32 {
|
||||
self.enterprise.as_ref().map_or(0, |e| e.license.accounts)
|
||||
}
|
||||
|
||||
fn log_license_details(&self) {
|
||||
pub fn log_license_details(&self) {
|
||||
if let Some(enterprise) = &self.enterprise {
|
||||
tracing::info!(
|
||||
licensed_to = enterprise.license.hostname,
|
|
@ -8,9 +8,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
use common::Core;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use store::{
|
||||
write::{
|
||||
|
@ -21,6 +18,8 @@ use store::{
|
|||
};
|
||||
use utils::{BlobHash, BLOB_HASH_LEN};
|
||||
|
||||
use crate::Core;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DeletedBlob<H, T, C> {
|
||||
pub hash: H,
|
||||
|
@ -32,22 +31,8 @@ pub struct DeletedBlob<H, T, C> {
|
|||
pub collection: C,
|
||||
}
|
||||
|
||||
pub trait Undelete: Sync + Send {
|
||||
fn hold_undelete(
|
||||
&self,
|
||||
batch: &mut BatchBuilder,
|
||||
collection: u8,
|
||||
blob_hash: &BlobHash,
|
||||
blob_size: usize,
|
||||
);
|
||||
fn list_deleted(
|
||||
&self,
|
||||
account_id: u32,
|
||||
) -> impl Future<Output = store::Result<Vec<DeletedBlob<BlobHash, u64, u8>>>> + Send;
|
||||
}
|
||||
|
||||
impl Undelete for Core {
|
||||
fn hold_undelete(
|
||||
impl Core {
|
||||
pub fn hold_undelete(
|
||||
&self,
|
||||
batch: &mut BatchBuilder,
|
||||
collection: u8,
|
||||
|
@ -71,7 +56,7 @@ impl Undelete for Core {
|
|||
}
|
||||
}
|
||||
|
||||
async fn list_deleted(
|
||||
pub async fn list_deleted(
|
||||
&self,
|
||||
account_id: u32,
|
||||
) -> store::Result<Vec<DeletedBlob<BlobHash, u64, u8>>> {
|
|
@ -35,8 +35,6 @@ use opentelemetry_sdk::{
|
|||
Resource,
|
||||
};
|
||||
use opentelemetry_semantic_conventions::resource::{SERVICE_NAME, SERVICE_VERSION};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use se_licensing::license::LicenseKey;
|
||||
use sieve::Sieve;
|
||||
use store::LookupStore;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
@ -49,6 +47,8 @@ use webhooks::{manager::WebhookEvent, WebhookPayload, WebhookType, Webhooks};
|
|||
|
||||
pub mod addresses;
|
||||
pub mod config;
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub mod enterprise;
|
||||
pub mod expr;
|
||||
pub mod listener;
|
||||
pub mod manager;
|
||||
|
@ -73,7 +73,7 @@ pub struct Core {
|
|||
pub imap: ImapConfig,
|
||||
pub web_hooks: Webhooks,
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub enterprise: Option<Enterprise>,
|
||||
pub enterprise: Option<enterprise::Enterprise>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -83,19 +83,6 @@ pub struct Network {
|
|||
pub url: IfBlock,
|
||||
}
|
||||
|
||||
// SPDX-SnippetBegin
|
||||
// SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
|
||||
// SPDX-License-Identifier: LicenseRef-SEL
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[derive(Clone)]
|
||||
pub struct Enterprise {
|
||||
pub license: LicenseKey,
|
||||
pub undelete_period: Option<std::time::Duration>,
|
||||
}
|
||||
|
||||
// SPDX-SnippetEnd
|
||||
|
||||
pub enum AuthResult<T> {
|
||||
Success(T),
|
||||
Failure(AuthFailureReason),
|
||||
|
|
|
@ -11,7 +11,6 @@ jmap_proto = { path = "../jmap-proto" }
|
|||
smtp = { path = "../smtp" }
|
||||
utils = { path = "../utils" }
|
||||
common = { path = "../common" }
|
||||
se_common = { path = "../se-common", optional = true }
|
||||
directory = { path = "../directory" }
|
||||
smtp-proto = { version = "0.1" }
|
||||
mail-parser = { version = "0.9", features = ["full_encoding", "serde_support", "ludicrous_mode"] }
|
||||
|
@ -61,4 +60,4 @@ quick-xml = "0.35"
|
|||
|
||||
[features]
|
||||
test_mode = []
|
||||
enterprise = ["se_common"]
|
||||
enterprise = []
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
|
||||
use common::enterprise::undelete::DeletedBlob;
|
||||
use directory::backend::internal::manage::ManageDirectory;
|
||||
use hyper::Method;
|
||||
use jmap_proto::{error::request::RequestError, types::collection::Collection};
|
||||
use mail_parser::{DateTime, MessageParser};
|
||||
use se_common::undelete::{DeletedBlob, Undelete};
|
||||
use serde_json::json;
|
||||
use store::write::{BatchBuilder, BlobOp, ValueClass};
|
||||
use utils::{url_params::UrlParams, BlobHash};
|
||||
|
|
|
@ -26,9 +26,6 @@ use serde::Serialize;
|
|||
use super::{http::ToHttpResponse, HttpRequest, HttpResponse, JsonResponse};
|
||||
use crate::{auth::AccessToken, JMAP};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use se_common::EnterpriseCore;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "error")]
|
||||
pub enum ManagementApiError {
|
||||
|
|
|
@ -24,9 +24,6 @@ use crate::{
|
|||
JMAP,
|
||||
};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use se_common::EnterpriseCore;
|
||||
|
||||
use super::{
|
||||
DeviceAuthResponse, FormData, OAuthCode, OAuthCodeRequest, CLIENT_ID_MAX_LEN, DEVICE_CODE_LEN,
|
||||
MAX_POST_LEN, USER_CODE_ALPHABET, USER_CODE_LEN,
|
||||
|
|
|
@ -13,8 +13,6 @@ use jmap_proto::{
|
|||
type_state::DataType,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use se_common::undelete::Undelete;
|
||||
use store::{
|
||||
ahash::AHashMap,
|
||||
roaring::RoaringBitmap,
|
||||
|
|
|
@ -26,7 +26,6 @@ managesieve = { path = "../managesieve" }
|
|||
common = { path = "../common" }
|
||||
directory = { path = "../directory" }
|
||||
utils = { path = "../utils" }
|
||||
se_common = { path = "../se-common", optional = true }
|
||||
tokio = { version = "1.23", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
|
||||
|
@ -44,4 +43,4 @@ rocks = ["store/rocks"]
|
|||
elastic = ["store/elastic"]
|
||||
s3 = ["store/s3"]
|
||||
redis = ["store/redis"]
|
||||
enterprise = ["se_common", "se_common/enterprise", "jmap/enterprise", "common/enterprise"]
|
||||
enterprise = ["jmap/enterprise", "common/enterprise"]
|
||||
|
|
|
@ -14,8 +14,6 @@ use imap::core::{ImapSessionManager, IMAP};
|
|||
use jmap::{api::JmapSessionManager, services::gossip::spawn::GossiperBuilder, JMAP};
|
||||
use managesieve::core::ManageSieveSessionManager;
|
||||
use pop3::Pop3SessionManager;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use se_common::EnterpriseCore;
|
||||
use smtp::core::{SmtpSessionManager, SMTP};
|
||||
use tokio::sync::mpsc;
|
||||
use utils::wait_for_shutdown;
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "se_common"
|
||||
version = "0.8.5"
|
||||
edition = "2021"
|
||||
license = "LicenseRef-SEL"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
store = { path = "../store" }
|
||||
utils = { path = "../utils" }
|
||||
tracing = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
|
||||
[features]
|
||||
test_mode = []
|
||||
enterprise = ["common/enterprise"]
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "se_licensing"
|
||||
version = "0.8.5"
|
||||
edition = "2021"
|
||||
license = "LicenseRef-SEL"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
ring = "0.17.8"
|
||||
|
||||
[features]
|
||||
test_mode = []
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: LicenseRef-SEL
|
||||
*
|
||||
* This file is subject to the Stalwart Enterprise License Agreement (SEL) and
|
||||
* is NOT open source software.
|
||||
*
|
||||
*/
|
||||
|
||||
pub mod license;
|
Loading…
Add table
Reference in a new issue