Legal holds, step 2: who a hold covers

A hold reaches an account by name, through any of its addresses'
domains, its groups or its tenant, as they are now, so an account added
to a held domain later is held too. An account that leaves a held
domain, group or tenant stays held: the registry write hook adds it to
the hold by name on every account change, whoever makes it (LH-2).
Server::holds_on answers for the deletion paths, from the store each
time so a hold binds every node at once.
This commit is contained in:
2026-09-27 19:33:06 -07:00
parent 5d2e35b2dc
commit 318783f444
5 changed files with 202 additions and 0 deletions
+51
View File
@@ -215,6 +215,57 @@ pub async fn test(test: &mut TestServer) {
.await;
assert_eq!(name, "error", "LH-13: a tenant administrator placed a hold: {response}");
// Test 7, LH-2: a hold on a domain reaches an account created there
// later, and keeps it by name when it moves to another domain
let held_domain = admin
.registry_create_object(Domain {
name: "held.example.net".to_string(),
is_enabled: true,
certificate_management: CertificateManagement::Manual,
dns_management: DnsManagement::Manual,
dkim_management: DkimManagement::Manual,
..Default::default()
})
.await;
let elsewhere = admin
.registry_create_object(Domain {
name: "elsewhere.example.net".to_string(),
is_enabled: true,
certificate_management: CertificateManagement::Manual,
dns_management: DnsManagement::Manual,
dkim_management: DkimManagement::Manual,
..Default::default()
})
.await;
let response = admin
.hold_set(json!({"reason": "Whole division", "create": {"d": {
"name": "Matter 5120", "scope": {"domains": [held_domain.to_string()]}}}}))
.await;
let domain_hold = response["created"]["d"]["id"]
.as_str()
.unwrap_or_else(|| panic!("LH-1 domain hold: {response}"))
.to_string();
let mover = admin
.create_user_account("[email protected]", "mover-secret-8812", "Mover", &[], vec![])
.await;
assert_eq!(
admin.hold_get(&domain_hold).await["scope"]["accounts"],
json!([]),
"LH-2: covered through the domain, not named yet"
);
admin
.registry_update_object(
ObjectType::Account,
mover.id(),
json!({Property::DomainId: elsewhere.to_string()}),
)
.await;
assert_eq!(
admin.hold_get(&domain_hold).await["scope"]["accounts"],
json!([mover.id_string()]),
"test 7, LH-2: the moved account escaped the hold"
);
// LH-10: release needs a reason, and a released hold stays, read-only
let response = admin
.hold_set(json!({"update": {hold_id.as_str(): {"released": true}}}))