Multi-tenancy (closes #212 closes #346 closes #582)

This commit is contained in:
mdecimus
2024-09-13 18:10:56 +02:00
parent d214468c54
commit ab77a0aca8
41 changed files with 1709 additions and 529 deletions

View File

@@ -6,7 +6,7 @@
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use common::auth::AccessToken;
use directory::Permission;
use directory::{backend::internal::manage::ManageDirectory, Permission, Type};
use hyper::Method;
use mail_auth::{
dmarc::URI,
@@ -21,6 +21,7 @@ use store::{
write::{key::DeserializeBigEndian, now, Bincode, QueueClass, ReportEvent, ValueClass},
Deserialize, IterateParams, ValueKey,
};
use trc::AddContext;
use utils::url_params::UrlParams;
use crate::{
@@ -111,6 +112,21 @@ impl JMAP {
) -> trc::Result<HttpResponse> {
let params = UrlParams::new(req.uri().query());
// Limit to tenant domains
let mut tenant_domains = None;
if self.core.is_enterprise_edition() {
if let Some(tenant) = access_token.tenant {
tenant_domains = self
.core
.storage
.data
.list_principals(None, Type::Domain.into(), tenant.id.into())
.await
.caused_by(trc::location!())?
.into();
}
}
match (
path.get(1).copied().unwrap_or_default(),
path.get(2).copied().map(decode_path_element),
@@ -156,32 +172,35 @@ impl JMAP {
IterateParams::new(from_key, to_key).ascending(),
|key, value| {
let message = Bincode::<queue::Message>::deserialize(value)?.inner;
let matches = !has_filters
|| (text
.as_ref()
.map(|text| {
message.return_path.contains(text)
|| message
.recipients
.iter()
.any(|r| r.address_lcase.contains(text))
})
.unwrap_or_else(|| {
from.as_ref()
.map_or(true, |from| message.return_path.contains(from))
&& to.as_ref().map_or(true, |to| {
let matches = tenant_domains
.as_ref()
.map_or(true, |domains| message.has_domain(domains))
&& (!has_filters
|| (text
.as_ref()
.map(|text| {
message.return_path.contains(text)
|| message
.recipients
.iter()
.any(|r| r.address_lcase.contains(text))
})
.unwrap_or_else(|| {
from.as_ref().map_or(true, |from| {
message.return_path.contains(from)
}) && to.as_ref().map_or(true, |to| {
message
.recipients
.iter()
.any(|r| r.address_lcase.contains(to))
})
})
&& before.as_ref().map_or(true, |before| {
message.next_delivery_event() < *before
})
&& after.as_ref().map_or(true, |after| {
message.next_delivery_event() > *after
}));
})
&& before.as_ref().map_or(true, |before| {
message.next_delivery_event() < *before
})
&& after.as_ref().map_or(true, |after| {
message.next_delivery_event() > *after
})));
if matches {
if offset == 0 {
@@ -230,6 +249,11 @@ impl JMAP {
.smtp
.read_message(queue_id.parse().unwrap_or_default())
.await
.filter(|message| {
tenant_domains
.as_ref()
.map_or(true, |domains| message.has_domain(domains))
})
{
Ok(JsonResponse::new(json!({
"data": Message::from(&message),
@@ -253,6 +277,11 @@ impl JMAP {
.smtp
.read_message(queue_id.parse().unwrap_or_default())
.await
.filter(|message| {
tenant_domains
.as_ref()
.map_or(true, |domains| message.has_domain(domains))
})
{
let prev_event = message.next_event().unwrap_or_default();
let mut found = false;
@@ -297,6 +326,11 @@ impl JMAP {
.smtp
.read_message(queue_id.parse().unwrap_or_default())
.await
.filter(|message| {
tenant_domains
.as_ref()
.map_or(true, |domains| message.has_domain(domains))
})
{
let mut found = false;
let prev_event = message.next_event().unwrap_or_default();
@@ -417,7 +451,10 @@ impl JMAP {
|key, _| {
if type_.map_or(true, |t| t == *key.last().unwrap()) {
let event = ReportEvent::deserialize(key)?;
if event.seq_id != 0
if tenant_domains
.as_ref()
.map_or(true, |domains| domains.contains(&event.domain))
&& event.seq_id != 0
&& domain.as_ref().map_or(true, |d| event.domain.contains(d))
{
if offset == 0 {
@@ -460,7 +497,11 @@ impl JMAP {
let mut result = None;
if let Some(report_id) = parse_queued_report_id(report_id.as_ref()) {
match report_id {
QueueClass::DmarcReportHeader(event) => {
QueueClass::DmarcReportHeader(event)
if tenant_domains
.as_ref()
.map_or(true, |domains| domains.contains(&event.domain)) =>
{
let mut rua = Vec::new();
if let Some(report) = self
.smtp
@@ -470,7 +511,11 @@ impl JMAP {
result = Report::dmarc(event, report, rua).into();
}
}
QueueClass::TlsReportHeader(event) => {
QueueClass::TlsReportHeader(event)
if tenant_domains
.as_ref()
.map_or(true, |domains| domains.contains(&event.domain)) =>
{
let mut rua = Vec::new();
if let Some(report) = self
.smtp
@@ -498,18 +543,28 @@ impl JMAP {
access_token.assert_has_permission(Permission::OutgoingReportDelete)?;
if let Some(report_id) = parse_queued_report_id(report_id.as_ref()) {
match report_id {
QueueClass::DmarcReportHeader(event) => {
let result = match report_id {
QueueClass::DmarcReportHeader(event)
if tenant_domains
.as_ref()
.map_or(true, |domains| domains.contains(&event.domain)) =>
{
self.smtp.delete_dmarc_report(event).await;
true
}
QueueClass::TlsReportHeader(event) => {
QueueClass::TlsReportHeader(event)
if tenant_domains
.as_ref()
.map_or(true, |domains| domains.contains(&event.domain)) =>
{
self.smtp.delete_tls_report(vec![event]).await;
true
}
_ => (),
}
_ => false,
};
Ok(JsonResponse::new(json!({
"data": true,
"data": result,
}))
.into_http_response())
} else {