diff --git a/src/commons/api/ca.rs b/src/commons/api/ca.rs index 027e7afb..f7f49364 100644 --- a/src/commons/api/ca.rs +++ b/src/commons/api/ca.rs @@ -20,7 +20,7 @@ use rpki::uri; use rpki::x509::{Serial, Time}; use crate::commons::api::publication::Publish; -use crate::commons::api::{publication, RoaAggregateKey}; +use crate::commons::api::{publication, Entitlements, RoaAggregateKey}; use crate::commons::api::{ Base64, ChildHandle, ErrorResponse, Handle, HexEncodedHash, IssuanceRequest, ListReply, ParentCaContact, ParentHandle, RepositoryContact, RequestResourceLimit, RoaDefinition, @@ -1492,6 +1492,7 @@ pub enum ParentKindInfo { } //------------ ParentInfo ---------------------------------------------------- + #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct ParentInfo { handle: ParentHandle, @@ -1515,6 +1516,122 @@ impl fmt::Display for ParentInfo { } } +//------------ ParentStatus -------------------------------------------------- + +pub struct AllParentStatuses(HashMap); + +impl AllParentStatuses { + pub fn set_failure(&mut self, ca: &Handle, parent: &ParentHandle, error: ErrorResponse) { + let status = self.get_mut_status(ca); + status.set_failure(parent, error); + } + + pub fn set_last_updated(&mut self, ca: &Handle, parent: &ParentHandle) { + self.get_mut_status(ca).set_last_updated(parent); + } + + pub fn set_entitlements(&mut self, ca: &Handle, parent: &ParentHandle, entitlements: &Entitlements) { + self.get_mut_status(ca).set_entitlements(parent, entitlements) + } + + fn get_mut_status(&mut self, ca: &Handle) -> &mut ParentStatuses { + if !self.0.contains_key(ca) { + self.0.insert(ca.clone(), ParentStatuses::default()); + } + + self.0.get_mut(ca).unwrap() + } +} + +impl Default for AllParentStatuses { + fn default() -> Self { + AllParentStatuses(HashMap::new()) + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ParentStatuses(HashMap); + +impl ParentStatuses { + fn set_failure(&mut self, parent: &ParentHandle, error: ErrorResponse) { + self.get_mut_status(parent).set_failure(error); + } + + fn set_entitlements(&mut self, parent: &ParentHandle, entitlements: &Entitlements) { + self.get_mut_status(parent).set_entitlements(entitlements); + } + + fn set_last_updated(&mut self, parent: &ParentHandle) { + self.get_mut_status(parent).set_last_updated(); + } + + fn get_mut_status(&mut self, parent: &ParentHandle) -> &mut ParentStatus { + if !self.0.contains_key(parent) { + self.0.insert(parent.clone(), ParentStatus::default()); + } + + self.0.get_mut(parent).unwrap() + } +} + +impl Default for ParentStatuses { + fn default() -> Self { + ParentStatuses(HashMap::new()) + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ParentStatus { + last_exchange: Option, + resources: HashMap, +} + +impl ParentStatus { + fn set_failure(&mut self, error: ErrorResponse) { + self.last_exchange = Some(ParentExchange { + time: Time::now(), + result: ParentExchangeResult::Failure(error), + }) + } + + fn set_entitlements(&mut self, entitlements: &Entitlements) { + self.set_last_updated(); + self.resources = entitlements + .classes() + .iter() + .map(|rc| (rc.class_name().clone(), rc.resource_set().clone())) + .collect(); + } + + fn set_last_updated(&mut self) { + self.last_exchange = Some(ParentExchange { + time: Time::now(), + result: ParentExchangeResult::Success, + }); + } +} + +impl Default for ParentStatus { + fn default() -> Self { + ParentStatus { + last_exchange: None, + resources: HashMap::new(), + } + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ParentExchange { + time: Time, + result: ParentExchangeResult, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum ParentExchangeResult { + Success, + Failure(ErrorResponse), +} + //------------ CertAuthInfo -------------------------------------------------- /// This type represents the details of a CertAuth that need diff --git a/src/commons/error.rs b/src/commons/error.rs index c9164be8..c825ee28 100644 --- a/src/commons/error.rs +++ b/src/commons/error.rs @@ -16,6 +16,7 @@ use crate::commons::api::{ }; use crate::commons::eventsourcing::AggregateStoreError; use crate::commons::remote::rfc6492; +use crate::commons::remote::rfc6492::NotPerformedResponse; use crate::commons::remote::rfc8181; use crate::commons::remote::rfc8181::ReportErrorCode; use crate::commons::util::httpclient; @@ -288,6 +289,9 @@ pub enum Error { #[display(fmt = "RFC 6492 Issue: {}", _0)] Rfc6492(rfc6492::Error), + #[display(fmt = "RFC 6492 Not Performed: {}", _0)] + Rfc6492NotPerformed(NotPerformedResponse), + #[display(fmt = "Invalid CSR received: {}", _0)] Rfc6492InvalidCsrSent(String), @@ -592,6 +596,7 @@ impl Error { // RFC6492 (requesting resources, not on JSON api) //----------------------------------------------------------------- Error::Rfc6492(e) => ErrorResponse::new("rfc6492-protocol", &self).with_cause(e), + Error::Rfc6492NotPerformed(e) => ErrorResponse::new("rfc6492-not-performed-response", &self).with_cause(e), Error::Rfc6492InvalidCsrSent(e) => ErrorResponse::new("rfc6492-invalid-csr", &self).with_cause(e), Error::Rfc6492SignatureInvalid => ErrorResponse::new("rfc6492-invalid-signature", &self), diff --git a/src/daemon/ca/server.rs b/src/daemon/ca/server.rs index 3e40d5cf..760c0424 100644 --- a/src/daemon/ca/server.rs +++ b/src/daemon/ca/server.rs @@ -10,11 +10,11 @@ use rpki::crypto::KeyIdentifier; use rpki::uri; use crate::commons::api::{ - self, AddChildRequest, Base64, CaCommandDetails, CaCommandResult, CertAuthList, CertAuthSummary, ChildAuthRequest, - ChildCaInfo, ChildHandle, CommandHistory, CommandHistoryCriteria, Entitlements, Handle, IssuanceRequest, - IssuanceResponse, IssuedCert, ListReply, ParentCaContact, ParentCaReq, ParentHandle, PublishDelta, RcvdCert, - RepoInfo, RepositoryContact, ResourceClassName, ResourceSet, RevocationRequest, RevocationResponse, StoredEffect, - UpdateChildRequest, + self, AddChildRequest, AllParentStatuses, Base64, CaCommandDetails, CaCommandResult, CertAuthList, CertAuthSummary, + ChildAuthRequest, ChildCaInfo, ChildHandle, CommandHistory, CommandHistoryCriteria, Entitlements, ErrorResponse, + Handle, IssuanceRequest, IssuanceResponse, IssuedCert, ListReply, ParentCaContact, ParentCaReq, ParentHandle, + PublishDelta, RcvdCert, RepoInfo, RepositoryContact, ResourceClassName, ResourceSet, RevocationRequest, + RevocationResponse, StoredEffect, UpdateChildRequest, }; use crate::commons::error::Error; use crate::commons::eventsourcing::{Aggregate, AggregateStore, CommandKey, DiskAggregateStore}; @@ -36,6 +36,7 @@ use crate::daemon::mq::EventQueueListener; pub struct CaServer { signer: Arc>, ca_store: Arc>>, + parents_statuses: Arc>, rfc8181_log_dir: Option, rfc6492_log_dir: Option, } @@ -56,6 +57,7 @@ impl CaServer { Ok(CaServer { signer, ca_store: Arc::new(ca_store), + parents_statuses: Arc::new(RwLock::new(AllParentStatuses::default())), rfc6492_log_dir: rfc6492_log_dir.cloned(), rfc8181_log_dir: rfc8181_log_dir.cloned(), }) @@ -510,13 +512,20 @@ impl CaServer { // No repo set, yet. So, skip updating. Ok(()) } else { - let entitlements = self.get_entitlements_from_parent(handle, parent).await?; + match self.get_entitlements_from_parent(handle, parent).await { + Err(e) => { + self.set_parent_status_failure(handle, parent, e.to_error_response()); + Err(e) + } + Ok(entitlements) => { + self.set_parent_status_entitlements(handle, parent, &entitlements); + if !self.update_resource_classes(handle, parent.clone(), entitlements)? { + return Ok(()); // Nothing to do + } - if !self.update_resource_classes(handle, parent.clone(), entitlements)? { - return Ok(()); // Nothing to do + Ok(()) // Pending requests will be picked up by the scheduler. + } } - - Ok(()) // Pending requests will be picked up by the scheduler. } } } @@ -568,8 +577,19 @@ impl CaServer { .await } ParentCaContact::Rfc6492(parent_res) => { - self.send_revoke_requests_rfc6492(revoke_requests, child.id_key(), parent_res) + match self + .send_revoke_requests_rfc6492(revoke_requests, child.id_key(), parent_res) .await + { + Err(e) => { + self.set_parent_status_failure(handle, parent, e.to_error_response()); + Err(e) + } + Ok(res) => { + self.set_parent_status_updated(handle, parent); + Ok(res) + } + } } } } @@ -628,17 +648,15 @@ impl CaServer { let revoke = rfc6492::Message::revoke(sender, recipient, req.clone()); - match self + let response = self .send_rfc6492_and_validate_response(signing_key, parent_res, revoke.into_bytes(), Some(cms_logger)) - .await - { - Err(e) => error!("Could not send/validate revoke: {}", e), - Ok(response) => match response { - rfc6492::Res::Revoke(revoke_response) => revocations.push(revoke_response), - rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), - rfc6492::Res::List(_) => error!("List response to revoke request??"), - rfc6492::Res::Issue(_) => error!("Issue response to revoke request??"), - }, + .await?; + + match response { + rfc6492::Res::Revoke(revoke_response) => revocations.push(revoke_response), + rfc6492::Res::NotPerformed(e) => return Err(Error::Rfc6492NotPerformed(e)), + rfc6492::Res::List(_) => return Err(Error::custom("Got a List response to revoke request??")), + rfc6492::Res::Issue(_) => return Err(Error::custom("Issue response to revoke request??")), } } @@ -656,8 +674,19 @@ impl CaServer { ParentCaContact::Ta(_) => Err(Error::TaNotAllowed), ParentCaContact::Embedded => self.send_cert_requests_embedded(cert_requests, handle, parent), ParentCaContact::Rfc6492(parent_res) => { - self.send_cert_requests_rfc6492(cert_requests, child.id_key(), &parent_res) + match self + .send_cert_requests_rfc6492(cert_requests, child.id_key(), &parent_res) .await + { + Err(e) => { + self.set_parent_status_failure(handle, parent, e.to_error_response()); + Err(e) + } + Ok(res) => { + self.set_parent_status_updated(handle, parent); + Ok(res) + } + } } }?; @@ -724,20 +753,18 @@ impl CaServer { let issue = rfc6492::Message::issue(sender, recipient, req); - match self + let response = self .send_rfc6492_and_validate_response(signing_key, parent_res, issue.into_bytes(), Some(cms_logger)) - .await - { - Err(e) => error!("Could not send/validate csr: {}", e), - Ok(response) => match response { - rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), - rfc6492::Res::Issue(issue_response) => { - let (_, _, _, issued) = issue_response.unwrap(); - issued_certs.push(issued); - } - rfc6492::Res::List(_) => error!("List reply to issue request??"), - rfc6492::Res::Revoke(_) => error!("Revoke reply to issue request??"), - }, + .await?; + + match response { + rfc6492::Res::NotPerformed(e) => return Err(Error::Rfc6492NotPerformed(e)), + rfc6492::Res::Issue(issue_response) => { + let (_, _, _, issued) = issue_response.unwrap(); + issued_certs.push(issued); + } + rfc6492::Res::List(_) => return Err(Error::custom("List reply to issue request??")), + rfc6492::Res::Revoke(_) => return Err(Error::custom("Revoke reply to issue request??")), } } @@ -843,6 +870,21 @@ impl CaServer { .into_reply() .map_err(Error::custom) } + + fn set_parent_status_failure(&self, ca: &Handle, parent: &ParentHandle, error: ErrorResponse) { + self.parents_statuses.write().unwrap().set_failure(ca, parent, error); + } + + fn set_parent_status_entitlements(&self, ca: &Handle, parent: &ParentHandle, entitlements: &Entitlements) { + self.parents_statuses + .write() + .unwrap() + .set_entitlements(ca, parent, entitlements); + } + + fn set_parent_status_updated(&self, ca: &Handle, parent: &ParentHandle) { + self.parents_statuses.write().unwrap().set_last_updated(ca, parent); + } } /// # Support sending publication messages, and verifying responses.