1 use std::{
2     collections::HashMap,
3     convert::{TryFrom, TryInto},
4     fmt,
5     path::PathBuf,
6 };
7 
8 use rpki::{
9     repository::{
10         crypto::KeyIdentifier,
11         roa::Roa,
12         x509::{Serial, Time},
13     },
14     uri,
15 };
16 
17 use crate::{
18     commons::{
19         api::{
20             rrdp::{CurrentObjects, DeltaElements, PublishElement, RrdpSession},
21             Base64, ChildHandle, Handle, HexEncodedHash, IssuanceRequest, IssuedCert, ObjectName, ParentCaContact,
22             ParentHandle, PublisherHandle, RcvdCert, RepoInfo, RepositoryContact, ResourceClassName, ResourceSet,
23             RevocationRequest, RevocationsDelta, RevokedObject, RoaAggregateKey, RtaName, TaCertDetails,
24         },
25         crypto::IdCert,
26         eventsourcing::StoredEvent,
27         remote::rfc8183,
28     },
29     daemon::ca::{self, CaEvt, CaEvtDet, PreparedRta, RouteAuthorization, SignedRta},
30     pubd::{
31         Publisher, RepositoryAccessEvent, RepositoryAccessEventDetails, RepositoryAccessInitDetails, RepositoryManager,
32         RrdpSessionReset, RrdpUpdate,
33     },
34     upgrades::UpgradeError,
35 };
36 
37 use super::*;
38 
39 pub type OldPubdEvt = StoredEvent<OldPubdEvtDet>;
40 pub type OldPubdInit = StoredEvent<OldPubdIniDet>;
41 pub type OldCaEvt = StoredEvent<OldCaEvtDet>;
42 
43 impl OldPubdEvt {
into_stored_pubd_event(self, version: u64) -> Result<RepositoryAccessEvent, UpgradeError>44     pub fn into_stored_pubd_event(self, version: u64) -> Result<RepositoryAccessEvent, UpgradeError> {
45         let (id, _, details) = self.unpack();
46         Ok(RepositoryAccessEvent::new(&id, version, details.into()))
47     }
48 
needs_migration(&self) -> bool49     pub fn needs_migration(&self) -> bool {
50         matches!(self.details(), OldPubdEvtDet::PublisherAdded(_, _))
51             || matches!(self.details(), OldPubdEvtDet::PublisherRemoved(_, _))
52     }
53 }
54 
55 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
56 pub struct OldPubdIniDet {
57     id_cert: IdCert,
58     session: RrdpSession,
59     rrdp_base_uri: uri::Https,
60     rsync_jail: uri::Rsync,
61     repo_base_dir: PathBuf,
62 }
63 
64 impl OldPubdIniDet {
unpack(self) -> (IdCert, RrdpSession, uri::Https, uri::Rsync, PathBuf)65     pub fn unpack(self) -> (IdCert, RrdpSession, uri::Https, uri::Rsync, PathBuf) {
66         (
67             self.id_cert,
68             self.session,
69             self.rrdp_base_uri,
70             self.rsync_jail,
71             self.repo_base_dir,
72         )
73     }
74 }
75 
76 impl fmt::Display for OldPubdIniDet {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result77     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78         write!(f, "old init")
79     }
80 }
81 
82 impl From<OldPubdIniDet> for RepositoryAccessInitDetails {
from(old: OldPubdIniDet) -> Self83     fn from(old: OldPubdIniDet) -> Self {
84         RepositoryAccessInitDetails::new(old.id_cert, old.rrdp_base_uri, old.rsync_jail)
85     }
86 }
87 
88 pub struct DerivedEmbeddedCaMigrationInfo {
89     pub child_request: rfc8183::ChildRequest,
90     pub parent_responses: HashMap<ChildHandle, rfc8183::ParentResponse>,
91 }
92 
93 impl OldCaEvt {
into_stored_ca_event( self, version: u64, repo_manager: &RepositoryManager, derived_embedded_ca_info_map: &HashMap<Handle, DerivedEmbeddedCaMigrationInfo>, ) -> Result<CaEvt, UpgradeError>94     pub fn into_stored_ca_event(
95         self,
96         version: u64,
97         repo_manager: &RepositoryManager,
98         derived_embedded_ca_info_map: &HashMap<Handle, DerivedEmbeddedCaMigrationInfo>,
99     ) -> Result<CaEvt, UpgradeError> {
100         let (id, _, details) = self.unpack();
101 
102         let event = match details {
103             OldCaEvtDet::RepoUpdated(contact) => {
104                 let contact = match contact {
105                     OldRepositoryContact::Rfc8181(res) => RepositoryContact::new(res),
106                     OldRepositoryContact::Embedded(_) => {
107                         let res = repo_manager.repository_response(&id)?;
108                         RepositoryContact::new(res)
109                     }
110                 };
111                 CaEvtDet::RepoUpdated { contact }
112             }
113             OldCaEvtDet::ParentAdded(parent, old_contact) => {
114                 let contact = match old_contact {
115                     OldParentCaContact::Rfc6492(res) => ParentCaContact::for_rfc6492(res),
116                     OldParentCaContact::Ta(details) => ParentCaContact::Ta(details),
117                     OldParentCaContact::Embedded => match derived_embedded_ca_info_map.get(&parent) {
118                         Some(info) => {
119                             let res = info.parent_responses.get(&id).ok_or_else(|| UpgradeError::Custom(
120                                 format!("Cannot upgrade CA '{}' using embedded parent '{}' which no longer has this CA as a child", id, parent)))?;
121                             ParentCaContact::for_rfc6492(res.clone())
122                         }
123                         None => {
124                             return Err(UpgradeError::Custom(format!(
125                                 "Cannot upgrade CA '{}' using embedded parent '{}' which is no longer present",
126                                 id, parent
127                             )))
128                         }
129                     },
130                 };
131                 CaEvtDet::ParentAdded { parent, contact }
132             }
133             OldCaEvtDet::ChildAdded(child, old_details) => {
134                 let (resources, id_cert_opt) = (old_details.resources, old_details.id_cert);
135 
136                 let id_cert = match id_cert_opt {
137                     Some(id_cert) => id_cert,
138                     None => {
139                         let child_info = derived_embedded_ca_info_map.get(&child).ok_or_else(|| {
140                             UpgradeError::Custom(format!(
141                                 "Cannot upgrade CA {}, embedded child {} is no longer present",
142                                 id, child
143                             ))
144                         })?;
145 
146                         child_info.child_request.id_cert().clone()
147                     }
148                 };
149 
150                 CaEvtDet::ChildAdded {
151                     child,
152                     id_cert,
153                     resources,
154                 }
155             }
156             _ => details.try_into()?,
157         };
158 
159         Ok(CaEvt::new(&id, version, event))
160     }
161 
needs_migration(&self) -> bool162     pub fn needs_migration(&self) -> bool {
163         !matches!(self.details(), OldCaEvtDet::ObjectSetUpdated(_, _))
164             && !matches!(self.details(), OldCaEvtDet::RepoCleaned(_))
165     }
166 }
167 
168 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
169 #[allow(clippy::large_enum_variant)]
170 #[serde(rename_all = "snake_case")]
171 pub enum OldCaEvtDet {
172     // Being a Trust Anchor
173     TrustAnchorMade(TaCertDetails),
174 
175     // Being a parent Events
176     ChildAdded(ChildHandle, OldChildDetails),
177     ChildCertificateIssued(ChildHandle, ResourceClassName, KeyIdentifier),
178     ChildKeyRevoked(ChildHandle, ResourceClassName, KeyIdentifier),
179     ChildCertificatesUpdated(ResourceClassName, ChildCertificateUpdates),
180     ChildUpdatedIdCert(ChildHandle, IdCert),
181     ChildUpdatedResources(ChildHandle, ResourceSet),
182     ChildRemoved(ChildHandle),
183 
184     // Being a child Events
185     IdUpdated(Rfc8183Id),
186     ParentAdded(ParentHandle, OldParentCaContact),
187     ParentUpdated(ParentHandle, OldParentCaContact),
188     ParentRemoved(ParentHandle, Vec<ObjectsDelta>),
189 
190     ResourceClassAdded(ResourceClassName, OldResourceClass),
191     ResourceClassRemoved(ResourceClassName, ObjectsDelta, ParentHandle, Vec<RevocationRequest>),
192     CertificateRequested(ResourceClassName, IssuanceRequest, KeyIdentifier),
193     CertificateReceived(ResourceClassName, KeyIdentifier, RcvdCert),
194 
195     // Key life cycle
196     KeyRollPendingKeyAdded(ResourceClassName, KeyIdentifier),
197     KeyPendingToNew(ResourceClassName, OldCertifiedKey, ObjectsDelta),
198     KeyPendingToActive(ResourceClassName, OldCertifiedKey, ObjectsDelta),
199     KeyRollActivated(ResourceClassName, RevocationRequest),
200     KeyRollFinished(ResourceClassName, ObjectsDelta),
201     UnexpectedKeyFound(ResourceClassName, RevocationRequest),
202 
203     // Route Authorizations
204     RouteAuthorizationAdded(RouteAuthorization),
205     RouteAuthorizationRemoved(RouteAuthorization),
206     RoasUpdated(ResourceClassName, RoaUpdates),
207 
208     // Publishing
209     ObjectSetUpdated(ResourceClassName, HashMap<KeyIdentifier, CurrentObjectSetDelta>),
210     RepoUpdated(OldRepositoryContact),
211     RepoCleaned(OldRepositoryContact),
212 
213     // Rta
214     RtaPrepared(RtaName, PreparedRta),
215     RtaSigned(RtaName, SignedRta),
216 }
217 
218 impl TryFrom<OldCaEvtDet> for CaEvtDet {
219     type Error = UpgradeError;
220 
try_from(old: OldCaEvtDet) -> Result<Self, Self::Error>221     fn try_from(old: OldCaEvtDet) -> Result<Self, Self::Error> {
222         let evt = match old {
223             OldCaEvtDet::TrustAnchorMade(ta_cert_details) => CaEvtDet::TrustAnchorMade { ta_cert_details },
224             OldCaEvtDet::ChildAdded(_child, _details) => {
225                 unreachable!("Add child must be converted with embedded children in mind")
226             }
227             OldCaEvtDet::ChildCertificateIssued(child, resource_class_name, ki) => CaEvtDet::ChildCertificateIssued {
228                 child,
229                 resource_class_name,
230                 ki,
231             },
232             OldCaEvtDet::ChildKeyRevoked(child, resource_class_name, ki) => CaEvtDet::ChildKeyRevoked {
233                 child,
234                 resource_class_name,
235                 ki,
236             },
237             OldCaEvtDet::ChildCertificatesUpdated(resource_class_name, cert_updates) => {
238                 CaEvtDet::ChildCertificatesUpdated {
239                     resource_class_name,
240                     updates: cert_updates.into(),
241                 }
242             }
243             OldCaEvtDet::ChildUpdatedIdCert(child, id_cert) => CaEvtDet::ChildUpdatedIdCert { child, id_cert },
244             OldCaEvtDet::ChildUpdatedResources(child, resources) => {
245                 CaEvtDet::ChildUpdatedResources { child, resources }
246             }
247             OldCaEvtDet::ChildRemoved(child) => CaEvtDet::ChildRemoved { child },
248 
249             OldCaEvtDet::IdUpdated(id) => CaEvtDet::IdUpdated { id: id.into() },
250             OldCaEvtDet::ParentAdded(_parent, _contact) => {
251                 unreachable!("Parent Added event is migrated differently")
252             }
253             OldCaEvtDet::ParentUpdated(_parent, _contact) => {
254                 unreachable!("Parent Updated event is migrated differently")
255             }
256             OldCaEvtDet::ParentRemoved(parent, _delta) => CaEvtDet::ParentRemoved { parent },
257 
258             OldCaEvtDet::ResourceClassAdded(_rcn, rc) => rc.into_added_event()?,
259             OldCaEvtDet::ResourceClassRemoved(resource_class_name, _delta, parent, revoke_requests) => {
260                 CaEvtDet::ResourceClassRemoved {
261                     resource_class_name,
262                     parent,
263                     revoke_requests,
264                 }
265             }
266             OldCaEvtDet::CertificateRequested(resource_class_name, req, ki) => CaEvtDet::CertificateRequested {
267                 resource_class_name,
268                 req,
269                 ki,
270             },
271             OldCaEvtDet::CertificateReceived(resource_class_name, ki, rcvd_cert) => CaEvtDet::CertificateReceived {
272                 resource_class_name,
273                 ki,
274                 rcvd_cert,
275             },
276 
277             OldCaEvtDet::KeyRollPendingKeyAdded(resource_class_name, pending_key_id) => {
278                 CaEvtDet::KeyRollPendingKeyAdded {
279                     resource_class_name,
280                     pending_key_id,
281                 }
282             }
283             OldCaEvtDet::KeyPendingToNew(resource_class_name, new_key, _delta) => CaEvtDet::KeyPendingToNew {
284                 resource_class_name,
285                 new_key: new_key.into(),
286             },
287             OldCaEvtDet::KeyPendingToActive(resource_class_name, current_key, _delta) => CaEvtDet::KeyPendingToActive {
288                 resource_class_name,
289                 current_key: current_key.into(),
290             },
291             OldCaEvtDet::KeyRollActivated(resource_class_name, revoke_req) => CaEvtDet::KeyRollActivated {
292                 resource_class_name,
293                 revoke_req,
294             },
295             OldCaEvtDet::KeyRollFinished(resource_class_name, _delta) => {
296                 CaEvtDet::KeyRollFinished { resource_class_name }
297             }
298             OldCaEvtDet::UnexpectedKeyFound(resource_class_name, revoke_req) => CaEvtDet::UnexpectedKeyFound {
299                 resource_class_name,
300                 revoke_req,
301             },
302 
303             OldCaEvtDet::RouteAuthorizationAdded(auth) => CaEvtDet::RouteAuthorizationAdded { auth },
304             OldCaEvtDet::RouteAuthorizationRemoved(auth) => CaEvtDet::RouteAuthorizationRemoved { auth },
305             OldCaEvtDet::RoasUpdated(resource_class_name, updates) => CaEvtDet::RoasUpdated {
306                 resource_class_name,
307                 updates: updates.try_into()?,
308             },
309 
310             OldCaEvtDet::ObjectSetUpdated(_, _) => unreachable!("This event must not be migrated"),
311 
312             OldCaEvtDet::RepoUpdated(_contact) => {
313                 unreachable!("Repo Updated is migrated with the embedded repo context")
314             }
315             OldCaEvtDet::RepoCleaned(_contact) => unreachable!("This event must not be migrated"),
316 
317             OldCaEvtDet::RtaPrepared(name, prepared) => CaEvtDet::RtaPrepared { name, prepared },
318             OldCaEvtDet::RtaSigned(name, rta) => CaEvtDet::RtaSigned { name, rta },
319         };
320         Ok(evt)
321     }
322 }
323 
324 impl fmt::Display for OldCaEvtDet {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result325     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
326         write!(f, "pre 0.9.0 event")
327     }
328 }
329 
330 /// Describes an update to the set of ROAs under a ResourceClass.
331 #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
332 pub struct ChildCertificateUpdates {
333     issued: Vec<IssuedCert>,
334     removed: Vec<KeyIdentifier>,
335 }
336 
337 impl From<ChildCertificateUpdates> for ca::ChildCertificateUpdates {
from(old: ChildCertificateUpdates) -> Self338     fn from(old: ChildCertificateUpdates) -> Self {
339         ca::ChildCertificateUpdates::new(old.issued, old.removed, vec![], vec![])
340     }
341 }
342 
343 impl ChildCertificateUpdates {
unpack(self) -> (Vec<IssuedCert>, Vec<KeyIdentifier>)344     pub fn unpack(self) -> (Vec<IssuedCert>, Vec<KeyIdentifier>) {
345         (self.issued, self.removed)
346     }
347 }
348 
349 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
350 pub struct ObjectsDelta {
351     ca_repo: uri::Rsync,
352     added: Vec<AddedObject>,
353     updated: Vec<UpdatedObject>,
354     withdrawn: Vec<WithdrawnObject>,
355 }
356 
357 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
358 pub struct AddedObject {
359     name: ObjectName,
360     object: CurrentObject,
361 }
362 
363 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
364 pub struct UpdatedObject {
365     name: ObjectName,
366     object: CurrentObject,
367     old: HexEncodedHash,
368 }
369 
370 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
371 pub struct WithdrawnObject {
372     name: ObjectName,
373     hash: HexEncodedHash,
374 }
375 
376 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
377 pub struct CurrentObjectSetDelta {
378     pub number: u64,
379     pub revocations_delta: RevocationsDelta,
380     pub manifest_info: OldManifestInfo,
381     pub crl_info: OldCrlInfo,
382     pub objects_delta: ObjectsDelta,
383 }
384 
385 // Describes an update to the set of ROAs under a ResourceClass.
386 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
387 pub struct RoaUpdates {
388     #[serde(skip_serializing_if = "HashMap::is_empty", default = "HashMap::new")]
389     updated: HashMap<RouteAuthorization, RoaInfo>,
390 
391     #[serde(skip_serializing_if = "HashMap::is_empty", default = "HashMap::new")]
392     removed: HashMap<RouteAuthorization, RevokedObject>,
393 
394     #[serde(skip_serializing_if = "HashMap::is_empty", default = "HashMap::new")]
395     aggregate_updated: HashMap<RoaAggregateKey, AggregateRoaInfo>,
396 
397     #[serde(skip_serializing_if = "HashMap::is_empty", default = "HashMap::new")]
398     aggregate_removed: HashMap<RoaAggregateKey, RevokedObject>,
399 }
400 
401 impl TryFrom<RoaUpdates> for ca::RoaUpdates {
402     type Error = UpgradeError;
403 
try_from(old: RoaUpdates) -> Result<Self, UpgradeError>404     fn try_from(old: RoaUpdates) -> Result<Self, UpgradeError> {
405         let mut updates = ca::RoaUpdates::default();
406         for (auth, info) in old.updated {
407             let roa = info.roa()?;
408             let roa_info = ca::RoaInfo::new(roa, info.since);
409             updates.update(auth, roa_info);
410         }
411 
412         for (auth, revoke) in old.removed {
413             updates.remove(auth, revoke)
414         }
415 
416         for (agg_key, agg_info) in old.aggregate_updated {
417             let roa = agg_info.roa.roa()?;
418             let roa_info = ca::RoaInfo::new(roa, agg_info.roa.since);
419             let authorizations = agg_info.authorizations;
420             let agg = ca::AggregateRoaInfo::new(authorizations, roa_info);
421             updates.update_aggregate(agg_key, agg);
422         }
423 
424         for (agg_key, revoke) in old.aggregate_removed {
425             updates.remove_aggregate(agg_key, revoke);
426         }
427 
428         Ok(updates)
429     }
430 }
431 
432 impl RoaUpdates {
433     #[allow(clippy::type_complexity)]
unpack( self, ) -> ( HashMap<RouteAuthorization, RoaInfo>, HashMap<RouteAuthorization, RevokedObject>, HashMap<RoaAggregateKey, AggregateRoaInfo>, HashMap<RoaAggregateKey, RevokedObject>, )434     pub fn unpack(
435         self,
436     ) -> (
437         HashMap<RouteAuthorization, RoaInfo>,
438         HashMap<RouteAuthorization, RevokedObject>,
439         HashMap<RoaAggregateKey, AggregateRoaInfo>,
440         HashMap<RoaAggregateKey, RevokedObject>,
441     ) {
442         (
443             self.updated,
444             self.removed,
445             self.aggregate_updated,
446             self.aggregate_removed,
447         )
448     }
449 }
450 
451 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
452 pub struct RoaInfo {
453     pub object: CurrentObject,           // actual ROA
454     name: ObjectName,                    // Name for object in repo
455     since: Time,                         // first ROA in RC created
456     replaces: Option<OldReplacedObject>, // for revoking when renewing
457 }
458 
459 impl RoaInfo {
roa(&self) -> Result<Roa, UpgradeError>460     pub fn roa(&self) -> Result<Roa, UpgradeError> {
461         Roa::decode(self.object.content.to_bytes(), true).map_err(|_| UpgradeError::custom("Cannot parse existing ROA"))
462     }
463 }
464 
465 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
466 pub struct AggregateRoaInfo {
467     pub authorizations: Vec<RouteAuthorization>,
468 
469     #[serde(flatten)]
470     pub roa: RoaInfo,
471 }
472 
473 pub type OldCaIni = StoredEvent<OldCaIniDet>;
474 
475 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
476 pub struct OldCaIniDet {
477     id: Rfc8183Id,
478 
479     // The following two fields need to be kept to maintain data compatibility
480     // with Krill 0.4.2 installations.
481     //
482     // Newer versions of krill will no longer include these fields. I.e. there
483     // will be no default embedded repository, and trust anchors will be created
484     // through an explicit command and events.
485     #[serde(skip_serializing_if = "Option::is_none")]
486     info: Option<RepoInfo>,
487     #[serde(skip_serializing_if = "Option::is_none")]
488     ta_details: Option<TaCertDetails>,
489 }
490 
491 impl OldCaIniDet {
unpack(self) -> (Rfc8183Id, Option<RepoInfo>, Option<TaCertDetails>)492     pub fn unpack(self) -> (Rfc8183Id, Option<RepoInfo>, Option<TaCertDetails>) {
493         (self.id, self.info, self.ta_details)
494     }
495 }
496 
497 impl fmt::Display for OldCaIniDet {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result498     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
499         write!(f, "Pre 0.9.0 CA init")
500     }
501 }
502 
503 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
504 pub struct CurrentObject {
505     content: Base64,
506     serial: Serial,
507     expires: Time,
508 }
509 
510 impl CurrentObject {
content(&self) -> &Base64511     pub fn content(&self) -> &Base64 {
512         &self.content
513     }
514 }
515 
516 //================ Pubd
517 
518 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
519 #[allow(clippy::large_enum_variant)]
520 #[serde(rename_all = "snake_case")]
521 pub enum OldPubdEvtDet {
522     PublisherAdded(PublisherHandle, OldPublisher),
523     PublisherRemoved(PublisherHandle, RrdpUpdate),
524     Published(PublisherHandle, RrdpUpdate),
525     RrdpSessionReset(RrdpSessionReset),
526 }
527 
528 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
529 pub struct OldPublisher {
530     /// Used by remote RFC8181 publishers
531     pub id_cert: IdCert,
532 
533     /// Publication jail for this publisher
534     pub base_uri: uri::Rsync,
535 
536     /// All objects currently published by this publisher, by hash
537     pub current_objects: OldCurrentObjects,
538 }
539 
540 impl OldPublisher {
apply_delta(&mut self, delta: DeltaElements)541     pub fn apply_delta(&mut self, delta: DeltaElements) {
542         self.current_objects.apply_delta(delta);
543     }
544 }
545 
546 impl fmt::Display for OldPubdEvtDet {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result547     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
548         write!(f, "pre 0.9.0 event")
549     }
550 }
551 
552 impl From<OldPublisher> for Publisher {
from(old: OldPublisher) -> Self553     fn from(old: OldPublisher) -> Self {
554         Publisher::new(old.id_cert, old.base_uri)
555     }
556 }
557 
558 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
559 pub struct OldCurrentObjects(HashMap<HexEncodedHash, PublishElement>);
560 
561 impl OldCurrentObjects {
new(map: HashMap<HexEncodedHash, PublishElement>) -> Self562     pub fn new(map: HashMap<HexEncodedHash, PublishElement>) -> Self {
563         OldCurrentObjects(map)
564     }
apply_delta(&mut self, delta: DeltaElements)565     pub fn apply_delta(&mut self, delta: DeltaElements) {
566         let (publishes, updates, withdraws) = delta.unpack();
567 
568         for p in publishes {
569             let hash = p.base64().to_encoded_hash();
570             self.0.insert(hash, p);
571         }
572 
573         for u in updates {
574             self.0.remove(u.hash());
575             let p: PublishElement = u.into();
576             let hash = p.base64().to_encoded_hash();
577             self.0.insert(hash, p);
578         }
579 
580         for w in withdraws {
581             self.0.remove(w.hash());
582         }
583     }
584 }
585 
586 impl From<OldCurrentObjects> for CurrentObjects {
from(old: OldCurrentObjects) -> Self587     fn from(old: OldCurrentObjects) -> Self {
588         CurrentObjects::new(old.0)
589     }
590 }
591 
592 impl From<OldPubdEvtDet> for RepositoryAccessEventDetails {
from(old: OldPubdEvtDet) -> Self593     fn from(old: OldPubdEvtDet) -> Self {
594         match old {
595             OldPubdEvtDet::PublisherAdded(name, publisher) => RepositoryAccessEventDetails::PublisherAdded {
596                 name,
597                 publisher: publisher.into(),
598             },
599             OldPubdEvtDet::PublisherRemoved(name, _) => RepositoryAccessEventDetails::PublisherRemoved { name },
600             _ => unreachable!("no need to migrate these old events"),
601         }
602     }
603 }
604