DNS, DKIM and ACME improvements - part 4

This commit is contained in:
Maurus Decimus
2026-04-02 19:32:51 +02:00
parent c54ec2397a
commit 4a4dcfd7e3
37 changed files with 932 additions and 209 deletions

View File

@@ -12,8 +12,9 @@ use registry::{
enums::Permission,
prelude::{ObjectType, Property},
structs::{
self, Credential, CustomRoles, Domain, EmailAlias, GroupAccount, PasswordCredential,
Permissions, PermissionsList, Roles, UserAccount,
self, CertificateManagement, Credential, CustomRoles, DkimManagement, DnsManagement,
Domain, EmailAlias, GroupAccount, PasswordCredential, Permissions, PermissionsList,
Roles, UserAccount,
},
},
types::{list::List, map::Map},
@@ -244,6 +245,9 @@ impl Account {
self.registry_create_object(Domain {
is_enabled: true,
name: name.to_string(),
certificate_management: CertificateManagement::Manual,
dns_management: DnsManagement::Manual,
dkim_management: DkimManagement::Manual,
..Default::default()
})
.await

View File

@@ -407,7 +407,14 @@ fn remove_server_set_props(value: &mut serde_json::Value) {
.and_then(|v| v.as_str())
.is_some_and(|t| ["AppPassword", "ApiKey"].contains(&t));
obj.retain(|k, v| {
!(["createdAt", "credentialId", "retireAt"].contains(&k.as_str())
!([
"createdAt",
"credentialId",
"retireAt",
"accountKey",
"accountUri",
]
.contains(&k.as_str())
|| (is_app_pass && k == "secret")
|| (k == "memberTenantId" && v.is_null()))
});

View File

@@ -42,8 +42,8 @@ use registry::{
enums::{DataStoreType, EventPolicy, NetworkListenerProtocol, TracingLevel},
prelude::{Object, ObjectType, SocketAddr},
structs::{
Authentication, Certificate, Expression, Http, NetworkListener, PublicText,
SecretKeyFile, SecretText, Tracer, TracerStdout,
Authentication, Certificate, NetworkListener, PublicText, SecretKeyFile, SecretText,
Tracer, TracerStdout,
},
},
types::{EnumImpl, map::Map},
@@ -149,28 +149,12 @@ impl TestServerBuilder {
] {
this = this.with_listener(protocol, name, port, use_tls).await;
}
this.with_object(Http {
base_url: Expression {
else_: "'https://127.0.0.1:8899'".to_string(),
..Default::default()
},
..Default::default()
})
.await
this
}
pub async fn with_http_listener(self, port: u16) -> Self {
self.with_listener(NetworkListenerProtocol::Http, "jmap", port, true)
.await
.with_object(Http {
base_url: Expression {
else_: format!("'https://127.0.0.1:{}'", port),
..Default::default()
},
..Default::default()
})
.await
}
pub async fn with_smtp_listener(self, port: u16) -> Self {
@@ -216,7 +200,7 @@ impl TestServerBuilder {
}
self.insert_object(NetworkListener {
bind: Map::new(vec![
SocketAddr::from_str(&format!("127.0.0.1:{port}")).unwrap(),
SocketAddr::from_str(&format!("0.0.0.0:{port}")).unwrap(),
]),
name: name.to_string(),
protocol,
@@ -469,11 +453,15 @@ impl TestServer {
}
pub async fn wait_for_tasks(&self) {
wait_for_tasks(&self.server, false).await;
wait_for_tasks(&self.server, false, false).await;
}
pub async fn wait_for_tasks_skip_failures(&self) {
wait_for_tasks(&self.server, true).await;
wait_for_tasks(&self.server, false, true).await;
}
pub async fn wait_for_tasks_skip_not_due(&self) {
wait_for_tasks(&self.server, true, false).await;
}
pub async fn blob_expire_all(&self) {

View File

@@ -21,6 +21,7 @@ use registry::{
},
types::{EnumImpl, duration::Duration},
};
use store::write::now;
use store::{
Deserialize, IterateParams, ValueKey,
write::{TaskQueueClass, ValueClass},
@@ -153,7 +154,7 @@ fn build_search_store(typ: SearchStoreType, _path: &str) -> SearchStore {
}
}
pub async fn wait_for_tasks(server: &Server, skip_permanent_failures: bool) {
pub async fn wait_for_tasks(server: &Server, skip_not_due: bool, skip_permanent_failures: bool) {
let mut count = 0;
loop {
let mut has_index_tasks = None;
@@ -169,7 +170,9 @@ pub async fn wait_for_tasks(server: &Server, skip_permanent_failures: bool) {
.ascending(),
|_, value| {
let task = Task::deserialize(value)?;
if skip_permanent_failures && matches!(task.status(), TaskStatus::Failed(_)) {
if (skip_permanent_failures && matches!(task.status(), TaskStatus::Failed(_)))
|| (skip_not_due && task.due_timestamp() > now())
{
Ok(true)
} else {
has_index_tasks = Some(task);
@@ -195,7 +198,7 @@ pub async fn wait_for_tasks(server: &Server, skip_permanent_failures: bool) {
pub async fn assert_is_empty(server: &Server, include_registry: bool) {
// Wait for pending index tasks
wait_for_tasks(server, false).await;
wait_for_tasks(server, false, false).await;
// Assert is empty
store_assert_is_empty(

View File

@@ -1174,7 +1174,8 @@ fn flatten_xml(xml: &str) -> Vec<(String, String)> {
}
}
Event::GeneralRef(entity) => {
let value: Cow<str> = match entity.as_ref() {
let entity_slice: &[u8] = entity.as_ref();
let value: Cow<str> = match entity_slice {
b"lt" => "<".into(),
b"gt" => ">".into(),
b"amp" => "&".into(),