Registry testing - all tests passing

This commit is contained in:
Maurus Decimus
2026-03-27 16:49:26 +01:00
parent 72bc8c05ab
commit e451f037c8
26 changed files with 1174 additions and 1239 deletions

View File

@@ -4,9 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::sync::Arc;
use crate::{Server, auth::DomainCache};
use crate::{Server, auth::DomainCache, ipc::BroadcastEvent};
use registry::{
schema::{
prelude::{Object, ObjectType},
@@ -17,21 +15,23 @@ use registry::{
},
types::{datetime::UTCDateTime, id::ObjectId, list::List},
};
use std::sync::Arc;
use store::registry::write::{RegistryWrite, RegistryWriteResult};
use trc::AddContext;
use types::id::Id;
pub(crate) struct AccountWithId {
pub struct AccountWithId {
pub id: u32,
pub account: Account,
}
impl Server {
pub(crate) async fn synchronize_account(
pub async fn synchronize_account(
&self,
account: directory::Account,
) -> trc::Result<AccountWithId> {
let (local, domain) = self.validate_address(&account.email).await?;
match self
.account_id_from_parts(local, domain.id)
.await
@@ -159,6 +159,8 @@ impl Server {
enabled: true,
description: None,
});
self.invalidate_local_negative_account_cache(local, alias_domain.id);
}
}
let mut member_group_ids = Vec::with_capacity(account.groups.len());
@@ -182,10 +184,13 @@ impl Server {
member_group_ids: member_group_ids.into(),
member_tenant_id: domain.id_tenant.map(Id::from),
roles: UserRoles::User,
credentials: List::from_iter([Credential::Password(PasswordCredential {
secret: account.secret.unwrap_or_default(),
..Default::default()
})]),
credentials: List::from_iter(account.secret.map(|secret| {
Credential::Password(PasswordCredential {
credential_id: 0u64.into(),
secret,
..Default::default()
})
})),
..Default::default()
}));
@@ -206,10 +211,16 @@ impl Server {
.await
.caused_by(trc::location!())?
{
RegistryWriteResult::Success(id) => Ok(AccountWithId {
id: id.document_id(),
account: account.into(),
}),
RegistryWriteResult::Success(id) => {
self.invalidate_local_negative_account_cache(local, domain.id);
self.cluster_broadcast(BroadcastEvent::CacheInvalidateNegative)
.await;
Ok(AccountWithId {
id: id.document_id(),
account: account.into(),
})
}
failure => Err(trc::AuthEvent::Error
.into_err()
.caused_by(trc::location!())
@@ -220,7 +231,7 @@ impl Server {
}
}
pub(crate) async fn synchronize_group(&self, group: directory::Group) -> trc::Result<u32> {
pub async fn synchronize_group(&self, group: directory::Group) -> trc::Result<u32> {
let (local, domain) = self.validate_address(&group.email).await?;
match self
@@ -313,6 +324,8 @@ impl Server {
enabled: true,
description: None,
});
self.invalidate_local_negative_account_cache(local, alias_domain.id);
}
}
@@ -344,7 +357,11 @@ impl Server {
.await
.caused_by(trc::location!())?
{
RegistryWriteResult::Success(id) => Ok(id.document_id()),
RegistryWriteResult::Success(id) => {
self.invalidate_local_negative_account_cache(local, domain.id);
Ok(id.document_id())
}
failure => Err(trc::AuthEvent::Error
.into_err()
.caused_by(trc::location!())

View File

@@ -6,7 +6,7 @@
use crate::{
Server,
auth::EmailCache,
auth::{EmailAddressRef, EmailCache},
ipc::{BroadcastEvent, CacheInvalidation},
};
use ahash::AHashSet;
@@ -283,6 +283,13 @@ impl Server {
self.inner.cache.emails_negative.clear();
}
pub fn invalidate_local_negative_account_cache(&self, local_part: &str, domain_id: u32) {
self.inner
.cache
.emails_negative
.remove(&EmailAddressRef::new(local_part, domain_id));
}
pub async fn invalidate_local_caches(&self, changes: &[CacheInvalidation]) {
let cache = &self.inner.cache;

View File

@@ -103,13 +103,36 @@ impl Server {
}
// SPDX-SnippetEnd
// Obtain external directory, if configured
let directory = self
.get_directory_for_cached_domain(&domain)
.filter(|directory| directory.can_lookup_recipients());
if let Some(directory) = directory {
let address = if local_part.as_ref() == local_part_orig {
Cow::Borrowed(rcpt)
} else {
Cow::Owned(format!("{local_part}@{domain_part}"))
};
match directory.recipient(address.as_ref()).await? {
Recipient::Account(account) => {
self.synchronize_account(account).await?;
return Ok(RcptResolution::Accept);
}
Recipient::Group(group) => {
self.synchronize_group(group).await?;
return Ok(RcptResolution::Accept);
}
Recipient::Invalid => {}
}
}
// Try resolving address from registry
if let Some(address_type) = self
.rcpt_id_from_parts(local_part.as_ref(), domain.id)
.await?
{
match address_type {
EmailCache::Account(id) => {
EmailCache::Account(id) if directory.is_none() => {
if self.try_account(id).await?.is_some() {
return if local_part.as_ref() == local_part_orig {
Ok(RcptResolution::Accept)
@@ -135,29 +158,7 @@ impl Server {
.remove(&EmailAddressRef::new(local_part.as_ref(), domain.id));
}
}
}
}
// Obtain external directory, if configured
if let Some(directory) = self
.get_directory_for_cached_domain(&domain)
.filter(|directory| directory.can_lookup_recipients())
{
let address = if local_part.as_ref() == local_part_orig {
Cow::Borrowed(rcpt)
} else {
Cow::Owned(format!("{local_part}@{domain_part}"))
};
match directory.recipient(address.as_ref()).await? {
Recipient::Account(account) => {
self.synchronize_account(account).await?;
return Ok(RcptResolution::Accept);
}
Recipient::Group(group) => {
self.synchronize_group(group).await?;
return Ok(RcptResolution::Accept);
}
Recipient::Invalid => {}
_ => {}
}
}