diff --git a/crates/jmap/src/registry/mapping/domain.rs b/crates/jmap/src/registry/mapping/domain.rs index 471a46c1..1eca4f02 100644 --- a/crates/jmap/src/registry/mapping/domain.rs +++ b/crates/jmap/src/registry/mapping/domain.rs @@ -116,19 +116,22 @@ pub(crate) async fn validate_domain( domain.certificate_management, CertificateManagement::Automatic(_) ); - tasks.push(Task::DnsManagement(TaskDnsManagement { - domain_id: Id::default(), - update_records: Map::new( - details - .publish_records - .iter() - .filter(|&&r| r != DnsRecordType::Dkim || !will_trigger_dkim) - .copied() - .collect(), - ), - on_success_renew_certificate, - status: TaskStatus::now(), - })); + let update_records: Vec = details + .publish_records + .iter() + .filter(|&&r| r != DnsRecordType::Dkim || !will_trigger_dkim) + .copied() + .collect(); + // inbuxa: with nothing to publish and no certificate to order after, + // the task would only end as a failure (#304) + if !update_records.is_empty() || on_success_renew_certificate { + tasks.push(Task::DnsManagement(TaskDnsManagement { + domain_id: Id::default(), + update_records: Map::new(update_records), + on_success_renew_certificate, + status: TaskStatus::now(), + })); + } on_success_renew_certificate } else { if will_schedule_dkim { diff --git a/crates/registry/src/schema/structs_impl.rs b/crates/registry/src/schema/structs_impl.rs index c196e2b6..8a9f19c5 100644 --- a/crates/registry/src/schema/structs_impl.rs +++ b/crates/registry/src/schema/structs_impl.rs @@ -10000,10 +10000,8 @@ impl DnsManagementProperties { errors.push(ValidationError::required(Property::Origin)); } } - let value = &self.publish_records; - if value.len() < 1 { - errors.push(ValidationError::min_items(Property::PublishRecords, 1)); - } + // inbuxa: an empty list is allowed, for a domain whose DNS is only + // there for DNS-01 certificate orders (#304) errors.len() == neb } diff --git a/resources/schema/schema.json.gz b/resources/schema/schema.json.gz index 57647444..94a822a9 100644 Binary files a/resources/schema/schema.json.gz and b/resources/schema/schema.json.gz differ diff --git a/resources/schema/schema.json.sha256 b/resources/schema/schema.json.sha256 index e5c2f669..981115d7 100644 --- a/resources/schema/schema.json.sha256 +++ b/resources/schema/schema.json.sha256 @@ -1 +1 @@ -IEGR-V3thRHvq0ihWoPe6v1ZchB0_ToA6MpBv1XdRUw \ No newline at end of file +b8SgeFSpT5ldfueCVEGcMlEBj7vH6uuTu_ozHtRhzqo \ No newline at end of file diff --git a/tests/src/automation/acme.rs b/tests/src/automation/acme.rs index 1c0e93f2..8a48463b 100644 --- a/tests/src/automation/acme.rs +++ b/tests/src/automation/acme.rs @@ -403,6 +403,55 @@ pub async fn test(test: &TestServer) { account.registry_destroy_all(ObjectType::Certificate).await; account.registry_destroy_all(ObjectType::Task).await; + // A domain whose DNS is only there for DNS-01 publishes no records (#304) + let cert_only_domain_id = account + .registry_create_object(Domain { + name: "cert-only.org".to_string(), + certificate_management: CertificateManagement::Automatic( + CertificateManagementProperties { + acme_provider_id: dns_acme_id, + subject_alternative_names: Default::default(), + }, + ), + dkim_management: DkimManagement::Manual, + dns_management: DnsManagement::Automatic(DnsManagementProperties { + dns_server_id: pebble_dns_id, + publish_records: Map::new(vec![]), + ..Default::default() + }), + ..Default::default() + }) + .await; + test.wait_for_tasks_skip_not_due().await; + assert_certificates(account, &[&["*.cert-only.org", "cert-only.org"]]).await; + account.registry_destroy_all(ObjectType::Certificate).await; + account.registry_destroy_all(ObjectType::Task).await; + + // With nothing to publish and no certificate to order, no DNS task is + // queued just to fail + let no_records_domain_id = account + .registry_create_object(Domain { + name: "no-records.org".to_string(), + certificate_management: CertificateManagement::Manual, + dkim_management: DkimManagement::Manual, + dns_management: DnsManagement::Automatic(DnsManagementProperties { + dns_server_id: pebble_dns_id, + publish_records: Map::new(vec![]), + ..Default::default() + }), + ..Default::default() + }) + .await; + test.wait_for_tasks_skip_not_due().await; + assert!( + !account + .registry_get_all::() + .await + .into_iter() + .any(|(_, task)| matches!(task, Task::DnsManagement(_))), + "no DNS task for a domain with nothing to publish" + ); + // Test ACME using DNS-01 challenge let dns_acme_id = account .registry_create_object(AcmeProvider { @@ -821,10 +870,22 @@ pub async fn test(test: &TestServer) { account .registry_destroy( ObjectType::Domain, - [tls_domain_id, dns_domain_id, persist_domain_id], + [ + tls_domain_id, + dns_domain_id, + persist_domain_id, + cert_only_domain_id, + no_records_domain_id, + ], ) .await - .assert_destroyed(&[tls_domain_id, dns_domain_id, persist_domain_id]); + .assert_destroyed(&[ + tls_domain_id, + dns_domain_id, + persist_domain_id, + cert_only_domain_id, + no_records_domain_id, + ]); account.registry_destroy_all(ObjectType::DnsServer).await; account.registry_destroy_all(ObjectType::AcmeProvider).await; account.registry_destroy_all(ObjectType::Certificate).await;