1 use std::{collections::BTreeMap, fmt, str::FromStr};
2 
3 use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
4 
5 use rpki::repository::{crypto::KeyIdentifier, x509::Time};
6 
7 use crate::{
8     commons::{
9         api::{
10             ArgKey, ArgVal, AspaCustomer, AspaProvidersUpdate, ChildHandle, Handle, Label, Message, ParentHandle,
11             PublisherHandle, RequestResourceLimit, ResourceClassName, ResourceSet, RevocationRequest,
12             RoaDefinitionUpdates, RtaName, StorableParentContact,
13         },
14         eventsourcing::{CommandKey, CommandKeyError, StoredCommand, WithStorableDetails},
15         remote::rfc8183::ServiceUri,
16     },
17     daemon::ca::{self, DropReason},
18 };
19 
20 use super::AspaDefinitionUpdates;
21 
22 //------------ CaCommandDetails ----------------------------------------------
23 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
24 pub struct CaCommandDetails {
25     command: StoredCommand<StorableCaCommand>,
26     result: CaCommandResult,
27 }
28 
29 impl CaCommandDetails {
new(command: StoredCommand<StorableCaCommand>, result: CaCommandResult) -> Self30     pub fn new(command: StoredCommand<StorableCaCommand>, result: CaCommandResult) -> Self {
31         CaCommandDetails { command, result }
32     }
33 
command(&self) -> &StoredCommand<StorableCaCommand>34     pub fn command(&self) -> &StoredCommand<StorableCaCommand> {
35         &self.command
36     }
37 
effect(&self) -> &CaCommandResult38     pub fn effect(&self) -> &CaCommandResult {
39         &self.result
40     }
41 }
42 
43 impl fmt::Display for CaCommandDetails {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result44     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45         let command = self.command();
46         writeln!(
47             f,
48             "Time:   {}",
49             command.time().to_rfc3339_opts(SecondsFormat::Secs, true)
50         )?;
51         writeln!(f, "Action: {}", command.details().summary().msg)?;
52 
53         match self.effect() {
54             CaCommandResult::Error(msg) => writeln!(f, "Error:  {}", msg)?,
55             CaCommandResult::Events(events) => {
56                 writeln!(f, "Changes:")?;
57                 for evt in events {
58                     writeln!(f, "  {}", evt.details().to_string())?;
59                 }
60             }
61         }
62 
63         Ok(())
64     }
65 }
66 
67 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
68 pub enum CaCommandResult {
69     Error(String),
70     Events(Vec<ca::CaEvt>),
71 }
72 
73 impl CaCommandResult {
error(msg: String) -> Self74     pub fn error(msg: String) -> Self {
75         CaCommandResult::Error(msg)
76     }
events(events: Vec<ca::CaEvt>) -> Self77     pub fn events(events: Vec<ca::CaEvt>) -> Self {
78         CaCommandResult::Events(events)
79     }
80 }
81 
82 //------------ CommandHistory ------------------------------------------------
83 
84 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
85 pub struct CommandHistory {
86     offset: usize,
87     total: usize,
88     commands: Vec<CommandHistoryRecord>,
89 }
90 
91 impl CommandHistory {
new(offset: usize, total: usize, commands: Vec<CommandHistoryRecord>) -> Self92     pub fn new(offset: usize, total: usize, commands: Vec<CommandHistoryRecord>) -> Self {
93         CommandHistory {
94             offset,
95             total,
96             commands,
97         }
98     }
99 
offset(&self) -> usize100     pub fn offset(&self) -> usize {
101         self.offset
102     }
103 
total(&self) -> usize104     pub fn total(&self) -> usize {
105         self.total
106     }
107 
commands(&self) -> &Vec<CommandHistoryRecord>108     pub fn commands(&self) -> &Vec<CommandHistoryRecord> {
109         &self.commands
110     }
111 }
112 
113 impl fmt::Display for CommandHistory {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result114     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115         writeln!(f, "time::command::key::success")?;
116 
117         for command in self.commands() {
118             let success_string = match &command.effect {
119                 StoredEffect::Error { msg } => format!("ERROR -> {}", msg),
120                 StoredEffect::Success { .. } => "OK".to_string(),
121             };
122             writeln!(
123                 f,
124                 "{}::{} ::{}::{}",
125                 command.time().to_rfc3339_opts(SecondsFormat::Secs, true),
126                 command.summary.msg,
127                 command.key,
128                 success_string
129             )?;
130         }
131 
132         Ok(())
133     }
134 }
135 
136 //------------ CommandHistoryRecord ------------------------------------------
137 
138 /// A description of a command that was processed, and the events / or error
139 /// that followed. Does not include the full stored command details, but only
140 /// the summary which is shown in the history response.
141 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
142 pub struct CommandHistoryRecord {
143     pub key: String,
144     pub actor: String,
145     pub timestamp: i64,
146     pub handle: Handle,
147     pub version: u64,
148     pub sequence: u64,
149     pub summary: CommandSummary,
150     pub effect: StoredEffect,
151 }
152 
153 impl CommandHistoryRecord {
time(&self) -> Time154     pub fn time(&self) -> Time {
155         let seconds = self.timestamp / 1000;
156         let time = NaiveDateTime::from_timestamp(seconds, 0);
157         Time::from(DateTime::from_utc(time, Utc))
158     }
159 
resulting_version(&self) -> u64160     pub fn resulting_version(&self) -> u64 {
161         if let Some(versions) = self.effect.events() {
162             if let Some(last) = versions.last() {
163                 *last
164             } else {
165                 self.version
166             }
167         } else {
168             self.version
169         }
170     }
171 
command_key(&self) -> Result<CommandKey, CommandKeyError>172     pub fn command_key(&self) -> Result<CommandKey, CommandKeyError> {
173         CommandKey::from_str(&self.key)
174     }
175 }
176 
177 //------------ StoredEffect --------------------------------------------------
178 
179 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
180 #[serde(rename_all = "snake_case", tag = "result")]
181 pub enum StoredEffect {
182     Error { msg: String },
183     Success { events: Vec<u64> },
184 }
185 
186 impl StoredEffect {
successful(&self) -> bool187     pub fn successful(&self) -> bool {
188         match self {
189             StoredEffect::Error { .. } => false,
190             StoredEffect::Success { .. } => true,
191         }
192     }
193 
events(&self) -> Option<&Vec<u64>>194     pub fn events(&self) -> Option<&Vec<u64>> {
195         match self {
196             StoredEffect::Error { .. } => None,
197             StoredEffect::Success { events } => Some(events),
198         }
199     }
200 }
201 
202 //------------ CommandSummary ------------------------------------------------
203 
204 /// Generic command summary used to show command details in history in a way
205 /// that support internationalization.
206 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
207 pub struct CommandSummary {
208     pub msg: Message,
209     pub label: Label,
210     pub args: BTreeMap<ArgKey, ArgVal>,
211 }
212 
213 impl CommandSummary {
new(label: &str, msg: impl fmt::Display) -> Self214     pub fn new(label: &str, msg: impl fmt::Display) -> Self {
215         CommandSummary {
216             msg: msg.to_string(),
217             label: label.to_string(),
218             args: BTreeMap::new(),
219         }
220     }
221 
with_arg(mut self, key: &str, val: impl fmt::Display) -> Self222     pub fn with_arg(mut self, key: &str, val: impl fmt::Display) -> Self {
223         self.args.insert(key.to_string(), val.to_string());
224         self
225     }
226 
with_child(self, child: &ChildHandle) -> Self227     pub fn with_child(self, child: &ChildHandle) -> Self {
228         self.with_arg("child", child)
229     }
230 
with_parent(self, parent: &ParentHandle) -> Self231     pub fn with_parent(self, parent: &ParentHandle) -> Self {
232         self.with_arg("parent", parent)
233     }
234 
with_publisher(self, publisher: &PublisherHandle) -> Self235     pub fn with_publisher(self, publisher: &PublisherHandle) -> Self {
236         self.with_arg("publisher", publisher)
237     }
238 
with_id_ski(self, id: &str) -> Self239     pub fn with_id_ski(self, id: &str) -> Self {
240         self.with_arg("id_key", id)
241     }
242 
with_resources(self, resources: &ResourceSet) -> Self243     pub fn with_resources(self, resources: &ResourceSet) -> Self {
244         let summary = resources.summary();
245         self.with_arg("resources", resources)
246             .with_arg("asn_blocks", summary.asn_blocks())
247             .with_arg("ipv4_blocks", summary.ipv4_blocks())
248             .with_arg("ipv6_blocks", summary.ipv6_blocks())
249     }
250 
with_rcn(self, rcn: &ResourceClassName) -> Self251     pub fn with_rcn(self, rcn: &ResourceClassName) -> Self {
252         self.with_arg("class_name", rcn)
253     }
254 
with_key(self, ki: &KeyIdentifier) -> Self255     pub fn with_key(self, ki: &KeyIdentifier) -> Self {
256         self.with_arg("key", ki)
257     }
258 
with_parent_contact(self, contact: &StorableParentContact) -> Self259     pub fn with_parent_contact(self, contact: &StorableParentContact) -> Self {
260         self.with_arg("parent_contact", contact)
261     }
262 
with_seconds(self, seconds: i64) -> Self263     pub fn with_seconds(self, seconds: i64) -> Self {
264         self.with_arg("seconds", seconds)
265     }
266 
with_added(self, nr: usize) -> Self267     pub fn with_added(self, nr: usize) -> Self {
268         self.with_arg("added", nr)
269     }
270 
with_removed(self, nr: usize) -> Self271     pub fn with_removed(self, nr: usize) -> Self {
272         self.with_arg("removed", nr)
273     }
274 
with_service_uri(self, service_uri: &ServiceUri) -> Self275     pub fn with_service_uri(self, service_uri: &ServiceUri) -> Self {
276         self.with_arg("service_uri", service_uri)
277     }
278 
with_rta_name(self, name: &str) -> Self279     pub fn with_rta_name(self, name: &str) -> Self {
280         self.with_arg("rta_name", name)
281     }
282 }
283 
284 //------------ CommandHistoryCriteria ----------------------------------------
285 
286 /// Used to limit the scope when finding commands to show in the history.
287 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
288 pub struct CommandHistoryCriteria {
289     #[serde(skip_serializing_if = "Option::is_none")]
290     before: Option<i64>,
291     #[serde(skip_serializing_if = "Option::is_none")]
292     after: Option<i64>,
293     #[serde(skip_serializing_if = "Option::is_none")]
294     after_sequence: Option<u64>,
295     #[serde(skip_serializing_if = "Option::is_none")]
296     label_includes: Option<Vec<String>>,
297     #[serde(skip_serializing_if = "Option::is_none")]
298     label_excludes: Option<Vec<String>>,
299 
300     offset: usize,
301     #[serde(skip_serializing_if = "Option::is_none")]
302     rows_limit: Option<usize>,
303 }
304 
305 impl CommandHistoryCriteria {
set_excludes(&mut self, labels: &[&str])306     pub fn set_excludes(&mut self, labels: &[&str]) {
307         self.label_excludes = Some(labels.iter().map(|s| (*s).to_string()).collect());
308     }
309 
set_includes(&mut self, labels: &[&str])310     pub fn set_includes(&mut self, labels: &[&str]) {
311         self.label_includes = Some(labels.iter().map(|s| (*s).to_string()).collect());
312     }
313 
set_after(&mut self, timestamp: i64)314     pub fn set_after(&mut self, timestamp: i64) {
315         self.after = Some(timestamp);
316     }
317 
set_before(&mut self, timestamp: i64)318     pub fn set_before(&mut self, timestamp: i64) {
319         self.before = Some(timestamp);
320     }
321 
set_after_sequence(&mut self, sequence: u64)322     pub fn set_after_sequence(&mut self, sequence: u64) {
323         self.after_sequence = Some(sequence)
324     }
325 
set_rows(&mut self, rows: usize)326     pub fn set_rows(&mut self, rows: usize) {
327         self.rows_limit = Some(rows);
328     }
329 
set_unlimited_rows(&mut self)330     pub fn set_unlimited_rows(&mut self) {
331         self.rows_limit = None
332     }
333 
set_offset(&mut self, offset: usize)334     pub fn set_offset(&mut self, offset: usize) {
335         self.offset = offset;
336     }
337 
matches_timestamp_secs(&self, stamp: i64) -> bool338     pub fn matches_timestamp_secs(&self, stamp: i64) -> bool {
339         if let Some(before) = self.before {
340             if stamp > before {
341                 return false;
342             }
343         }
344         if let Some(after) = self.after {
345             if stamp < after {
346                 return false;
347             }
348         }
349         true
350     }
351 
matches_sequence(&self, sequence: u64) -> bool352     pub fn matches_sequence(&self, sequence: u64) -> bool {
353         match self.after_sequence {
354             None => true,
355             Some(seq_crit) => sequence > seq_crit,
356         }
357     }
358 
359     #[allow(clippy::ptr_arg)]
matches_label(&self, label: &Label) -> bool360     pub fn matches_label(&self, label: &Label) -> bool {
361         if let Some(includes) = &self.label_includes {
362             if !includes.contains(label) {
363                 return false;
364             }
365         }
366         if let Some(excludes) = &self.label_excludes {
367             if excludes.contains(label) {
368                 return false;
369             }
370         }
371 
372         true
373     }
374 
offset(&self) -> usize375     pub fn offset(&self) -> usize {
376         self.offset
377     }
378 
rows_limit(&self) -> Option<usize>379     pub fn rows_limit(&self) -> Option<usize> {
380         self.rows_limit
381     }
382 }
383 
384 impl Default for CommandHistoryCriteria {
default() -> Self385     fn default() -> Self {
386         CommandHistoryCriteria {
387             before: None,
388             after: None,
389             after_sequence: None,
390             label_includes: None,
391             label_excludes: None,
392             offset: 0,
393             rows_limit: Some(100),
394         }
395     }
396 }
397 
398 //------------ StorableCaCommand -------------------------------------------
399 
400 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
401 #[allow(clippy::large_enum_variant)]
402 #[serde(rename_all = "snake_case")]
403 #[serde(tag = "type")]
404 pub enum StorableCaCommand {
405     MakeTrustAnchor,
406     ChildAdd {
407         child: ChildHandle,
408         ski: String,
409         resources: ResourceSet,
410     },
411     ChildUpdateResources {
412         child: ChildHandle,
413         resources: ResourceSet,
414     },
415     ChildUpdateId {
416         child: ChildHandle,
417         ski: String,
418     },
419     ChildCertify {
420         child: ChildHandle,
421         resource_class_name: ResourceClassName,
422         limit: RequestResourceLimit,
423         ki: KeyIdentifier,
424     },
425     ChildRevokeKey {
426         child: ChildHandle,
427         revoke_req: RevocationRequest,
428     },
429     ChildRemove {
430         child: ChildHandle,
431     },
432     ChildSuspendInactive {
433         child: ChildHandle,
434     },
435     ChildUnsuspend {
436         child: ChildHandle,
437     },
438     GenerateNewIdKey,
439     AddParent {
440         parent: ParentHandle,
441         contact: StorableParentContact,
442     },
443     UpdateParentContact {
444         parent: ParentHandle,
445         contact: StorableParentContact,
446     },
447     RemoveParent {
448         parent: ParentHandle,
449     },
450     UpdateResourceEntitlements {
451         parent: ParentHandle,
452         entitlements: Vec<StorableRcEntitlement>,
453     },
454     UpdateRcvdCert {
455         resource_class_name: ResourceClassName,
456         resources: ResourceSet,
457     },
458     DropResourceClass {
459         resource_class_name: ResourceClassName,
460         reason: DropReason,
461     },
462     KeyRollInitiate {
463         older_than_seconds: i64,
464     },
465     KeyRollActivate {
466         staged_for_seconds: i64,
467     },
468     KeyRollFinish {
469         resource_class_name: ResourceClassName,
470     },
471     RoaDefinitionUpdates {
472         updates: RoaDefinitionUpdates,
473     },
474     ReissueBeforeExpiring,
475     ForceReissue,
476     AspasUpdate {
477         updates: AspaDefinitionUpdates,
478     },
479     AspasUpdateExisting {
480         customer: AspaCustomer,
481         update: AspaProvidersUpdate,
482     },
483     AspaRemove {
484         customer: AspaCustomer,
485     },
486     RepoUpdate {
487         service_uri: ServiceUri,
488     },
489     RtaPrepare {
490         name: RtaName,
491     },
492     RtaSign {
493         name: RtaName,
494     },
495     RtaCoSign {
496         name: RtaName,
497     },
498     Deactivate,
499 }
500 
501 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
502 pub struct StorableRcEntitlement {
503     pub resource_class_name: ResourceClassName,
504     pub resources: ResourceSet,
505 }
506 
507 impl WithStorableDetails for StorableCaCommand {
summary(&self) -> CommandSummary508     fn summary(&self) -> CommandSummary {
509         match self {
510             StorableCaCommand::MakeTrustAnchor => CommandSummary::new("cmd-ca-make-ta", &self),
511             StorableCaCommand::ChildAdd { child, ski, resources } => CommandSummary::new("cmd-ca-child-add", &self)
512                 .with_child(child)
513                 .with_id_ski(ski.as_ref())
514                 .with_resources(resources),
515             StorableCaCommand::ChildUpdateResources { child, resources } => {
516                 CommandSummary::new("cmd-ca-child-update-res", &self)
517                     .with_child(child)
518                     .with_resources(resources)
519             }
520             StorableCaCommand::ChildUpdateId { child, ski } => CommandSummary::new("cmd-ca-child-update-id", &self)
521                 .with_child(child)
522                 .with_id_ski(ski),
523             StorableCaCommand::ChildCertify {
524                 child,
525                 resource_class_name,
526                 ki,
527                 ..
528             } => CommandSummary::new("cmd-ca-child-certify", &self)
529                 .with_child(child)
530                 .with_rcn(resource_class_name)
531                 .with_key(ki),
532             StorableCaCommand::ChildRemove { child } => {
533                 CommandSummary::new("cmd-ca-child-remove", &self).with_child(child)
534             }
535             StorableCaCommand::ChildSuspendInactive { child } => {
536                 CommandSummary::new("cmd-ca-child-suspend-inactive", &self).with_child(child)
537             }
538             StorableCaCommand::ChildUnsuspend { child } => {
539                 CommandSummary::new("cmd-ca-child-unsuspend", &self).with_child(child)
540             }
541             StorableCaCommand::ChildRevokeKey { child, revoke_req } => {
542                 CommandSummary::new("cmd-ca-child-revoke", &self)
543                     .with_child(child)
544                     .with_rcn(revoke_req.class_name())
545                     .with_key(revoke_req.key())
546             }
547             StorableCaCommand::GenerateNewIdKey => CommandSummary::new("cmd-ca-generate-new-id", &self),
548             StorableCaCommand::AddParent { parent, contact } => CommandSummary::new("cmd-ca-parent-add", &self)
549                 .with_parent(parent)
550                 .with_parent_contact(contact),
551             StorableCaCommand::UpdateParentContact { parent, contact } => {
552                 CommandSummary::new("cmd-ca-parent-update", &self)
553                     .with_parent(parent)
554                     .with_parent_contact(contact)
555             }
556             StorableCaCommand::RemoveParent { parent } => {
557                 CommandSummary::new("cmd-ca-parent-remove", &self).with_parent(parent)
558             }
559             StorableCaCommand::UpdateResourceEntitlements { parent, .. } => {
560                 CommandSummary::new("cmd-ca-parent-entitlements", &self).with_parent(parent)
561             }
562             StorableCaCommand::UpdateRcvdCert {
563                 resource_class_name,
564                 resources,
565             } => CommandSummary::new("cmd-ca-rcn-receive", &self)
566                 .with_rcn(resource_class_name)
567                 .with_resources(resources),
568             StorableCaCommand::DropResourceClass {
569                 resource_class_name,
570                 reason,
571             } => CommandSummary::new("cmd-ca-rc-drop", &self)
572                 .with_rcn(resource_class_name)
573                 .with_arg("reason", reason),
574 
575             // Key rolls
576             StorableCaCommand::KeyRollInitiate { older_than_seconds } => {
577                 CommandSummary::new("cmd-ca-keyroll-init", &self).with_seconds(*older_than_seconds)
578             }
579             StorableCaCommand::KeyRollActivate { staged_for_seconds } => {
580                 CommandSummary::new("cmd-ca-keyroll-activate", &self).with_seconds(*staged_for_seconds)
581             }
582             StorableCaCommand::KeyRollFinish { resource_class_name } => {
583                 CommandSummary::new("cmd-ca-keyroll-finish", &self).with_rcn(resource_class_name)
584             }
585 
586             // ROA
587             StorableCaCommand::RoaDefinitionUpdates { updates } => CommandSummary::new("cmd-ca-roas-updated", &self)
588                 .with_added(updates.added().len())
589                 .with_removed(updates.removed().len()),
590 
591             // ASPA
592             StorableCaCommand::AspasUpdate { .. } => CommandSummary::new("cmd-ca-aspas-update", &self),
593             StorableCaCommand::AspasUpdateExisting { .. } => CommandSummary::new("cmd-ca-aspas-update-existing", &self),
594             StorableCaCommand::AspaRemove { .. } => CommandSummary::new("cmd-ca-aspas-remove", &self),
595 
596             // REPO
597             StorableCaCommand::RepoUpdate { service_uri } => {
598                 CommandSummary::new("cmd-ca-repo-update", &self).with_service_uri(service_uri)
599             }
600 
601             StorableCaCommand::ReissueBeforeExpiring => CommandSummary::new("cmd-ca-reissue-before-expiring", &self),
602             StorableCaCommand::ForceReissue => CommandSummary::new("cmd-ca-force-reissue", &self),
603 
604             // RTA
605             StorableCaCommand::RtaPrepare { name } => {
606                 CommandSummary::new("cmd-ca-rta-prepare", &self).with_rta_name(name)
607             }
608             StorableCaCommand::RtaSign { name } => CommandSummary::new("cmd-ca-rta-sign", &self).with_rta_name(name),
609             StorableCaCommand::RtaCoSign { name } => {
610                 CommandSummary::new("cmd-ca-rta-cosign", &self).with_rta_name(name)
611             }
612 
613             // Deactivation
614             StorableCaCommand::Deactivate => CommandSummary::new("cmd-ca-deactivate", &self),
615         }
616     }
617 }
618 
619 impl fmt::Display for StorableCaCommand {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result620     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
621         match self {
622             // ------------------------------------------------------------
623             // Becoming a trust anchor
624             // ------------------------------------------------------------
625             StorableCaCommand::MakeTrustAnchor => write!(f, "Turn into Trust Anchor"),
626 
627             // ------------------------------------------------------------
628             // Being a parent
629             // ------------------------------------------------------------
630             StorableCaCommand::ChildAdd { child, ski, resources } => write!(
631                 f,
632                 "Add child '{}' with RFC8183 key '{}' and resources '{}'",
633                 child,
634                 ski,
635                 resources.summary()
636             ),
637             StorableCaCommand::ChildUpdateResources { child, resources } => {
638                 write!(f, "Update resources for child '{}' to: {}", child, resources.summary())
639             }
640             StorableCaCommand::ChildUpdateId { child, ski } => {
641                 write!(f, "Update child '{}' RFC 8183 key '{}'", child, ski)
642             }
643             StorableCaCommand::ChildCertify { child, ki, .. } => {
644                 write!(f, "Issue certificate to child '{}' for key '{}'", child, ki)
645             }
646             StorableCaCommand::ChildRevokeKey { child, revoke_req } => write!(
647                 f,
648                 "Revoke certificates for child '{}' for key '{}' in RC {}",
649                 child,
650                 revoke_req.key(),
651                 revoke_req.class_name()
652             ),
653             StorableCaCommand::ChildRemove { child } => {
654                 write!(f, "Remove child '{}' and revoke & remove its certs", child)
655             }
656             StorableCaCommand::ChildSuspendInactive { child } => {
657                 write!(f, "Suspend inactive child '{}': stop publishing its certs", child)
658             }
659             StorableCaCommand::ChildUnsuspend { child } => {
660                 write!(f, "Unsuspend child '{}': publish its unexpired certs", child)
661             }
662 
663             // ------------------------------------------------------------
664             // Being a child (only allowed if this CA is not self-signed)
665             // ------------------------------------------------------------
666             StorableCaCommand::GenerateNewIdKey => write!(f, "Generate a new RFC8183 ID."),
667             StorableCaCommand::AddParent { parent, contact } => write!(f, "Add parent '{}' as '{}'", parent, contact),
668             StorableCaCommand::UpdateParentContact { parent, contact } => {
669                 write!(f, "Update contact for parent '{}' to '{}'", parent, contact)
670             }
671             StorableCaCommand::RemoveParent { parent } => write!(f, "Remove parent '{}'", parent),
672 
673             StorableCaCommand::UpdateResourceEntitlements { parent, entitlements } => {
674                 let mut summary = format!("Update entitlements under parent '{}': ", parent);
675 
676                 for entitlement in entitlements.iter() {
677                     summary.push_str(&format!(
678                         "{} => {} ",
679                         entitlement.resource_class_name, entitlement.resources
680                     ))
681                 }
682 
683                 write!(f, "{}", summary)
684             }
685             // Process a new certificate received from a parent.
686             StorableCaCommand::UpdateRcvdCert {
687                 resource_class_name,
688                 resources,
689             } => write!(
690                 f,
691                 "Update received cert in RC '{}', with resources '{}'",
692                 resource_class_name,
693                 resources.summary()
694             ),
695             StorableCaCommand::DropResourceClass {
696                 resource_class_name,
697                 reason,
698             } => write!(
699                 f,
700                 "Removing resource class '{}' because of reason: {}",
701                 resource_class_name, reason
702             ),
703 
704             // ------------------------------------------------------------
705             // Key rolls
706             // ------------------------------------------------------------
707             StorableCaCommand::KeyRollInitiate { older_than_seconds } => {
708                 write!(
709                     f,
710                     "Initiate key roll for keys older than '{}' seconds",
711                     older_than_seconds
712                 )
713             }
714             StorableCaCommand::KeyRollActivate { staged_for_seconds } => {
715                 write!(
716                     f,
717                     "Activate new keys staging longer than '{}' seconds",
718                     staged_for_seconds
719                 )
720             }
721 
722             StorableCaCommand::KeyRollFinish { resource_class_name } => {
723                 write!(f, "Retire old revoked key in RC '{}'", resource_class_name)
724             }
725 
726             // ------------------------------------------------------------
727             // ROA Support
728             // ------------------------------------------------------------
729             StorableCaCommand::RoaDefinitionUpdates { updates } => {
730                 write!(f, "Update ROAs",)?;
731                 if !updates.added().is_empty() {
732                     write!(f, "  ADD:",)?;
733                     for addition in updates.added() {
734                         write!(f, " {}", addition)?;
735                     }
736                 }
737                 if !updates.removed().is_empty() {
738                     write!(f, "  REMOVE:",)?;
739                     for rem in updates.removed() {
740                         write!(f, " {}", rem)?;
741                     }
742                 }
743                 Ok(())
744             }
745             StorableCaCommand::ReissueBeforeExpiring => {
746                 write!(f, "Automatically re-issue objects before they would expire")
747             }
748             StorableCaCommand::ForceReissue => {
749                 write!(f, "Force re-issuance of objects")
750             }
751 
752             // ------------------------------------------------------------
753             // ASPA Support
754             // ------------------------------------------------------------
755             StorableCaCommand::AspasUpdate { updates } => {
756                 write!(f, "{}", updates)
757             }
758             StorableCaCommand::AspasUpdateExisting { customer, update } => {
759                 write!(f, "update ASPA for customer AS: {} {}", customer, update)
760             }
761             StorableCaCommand::AspaRemove { customer } => {
762                 write!(f, "Remove ASPA for customer AS: {}", customer)
763             }
764 
765             // ------------------------------------------------------------
766             // Publishing
767             // ------------------------------------------------------------
768             StorableCaCommand::RepoUpdate { service_uri } => write!(f, "Update repo to server at: {}", service_uri),
769 
770             // ------------------------------------------------------------
771             // RTA
772             // ------------------------------------------------------------
773             StorableCaCommand::RtaPrepare { name } => write!(f, "RTA Prepare {}", name),
774             StorableCaCommand::RtaSign { name } => write!(f, "RTA Sign {}", name),
775             StorableCaCommand::RtaCoSign { name } => write!(f, "RTA Co-Sign {}", name),
776 
777             // ------------------------------------------------------------
778             // Deactivate
779             // ------------------------------------------------------------
780             StorableCaCommand::Deactivate => write!(f, "Deactivate CA"),
781         }
782     }
783 }
784 
785 //------------ StorableRepositoryCommand -----------------------------------
786 
787 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
788 #[allow(clippy::large_enum_variant)]
789 #[serde(rename_all = "snake_case", tag = "type")]
790 pub enum StorableRepositoryCommand {
791     AddPublisher { name: PublisherHandle },
792     RemovePublisher { name: PublisherHandle },
793 }
794 
795 impl WithStorableDetails for StorableRepositoryCommand {
summary(&self) -> CommandSummary796     fn summary(&self) -> CommandSummary {
797         match self {
798             StorableRepositoryCommand::AddPublisher { name } => {
799                 CommandSummary::new("pubd-publisher-add", &self).with_publisher(name)
800             }
801             StorableRepositoryCommand::RemovePublisher { name } => {
802                 CommandSummary::new("pubd-publisher-remove", &self).with_publisher(name)
803             }
804         }
805     }
806 }
807 
808 impl fmt::Display for StorableRepositoryCommand {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result809     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
810         match self {
811             StorableRepositoryCommand::AddPublisher { name } => {
812                 write!(f, "Added publisher '{}'", name)
813             }
814             StorableRepositoryCommand::RemovePublisher { name } => write!(f, "Removed publisher '{}'", name),
815         }
816     }
817 }
818