diff --git a/src/cli/client.rs b/src/cli/client.rs index 79b35d1f..8a46f16f 100644 --- a/src/cli/client.rs +++ b/src/cli/client.rs @@ -8,12 +8,12 @@ use rpki::uri; use crate::cli::options::{BulkCaCommand, CaCommand, Command, Options, PublishersCommand}; use crate::cli::report::{ApiResponse, ReportError}; use crate::commons::api::{ - CaRepoDetails, CertAuthInfo, ParentCaContact, PublisherDetails, PublisherList, Token, + CaRepoDetails, CertAuthInfo, ChildCaInfo, ParentCaContact, PublisherDetails, PublisherList, + Token, }; use crate::commons::remote::rfc8183; use crate::commons::util::httpclient; use crate::constants::KRILL_CLI_API_ENV; -use commons::api::ChildCaInfo; /// Command line tool for Krill admin tasks pub struct KrillClient { @@ -94,7 +94,7 @@ impl KrillClient { } CaCommand::ParentResponse(handle, child) => { - let uri = format!("api/v1/cas/{}/parent_contact/{}", handle, child); + let uri = format!("api/v1/cas/{}/children/{}/contact", handle, child); let info: ParentCaContact = self.get_json(&uri)?; Ok(ApiResponse::ParentCaContact(info)) } @@ -142,6 +142,12 @@ impl KrillClient { Ok(ApiResponse::Empty) } + CaCommand::MyParentCaContact(handle, parent) => { + let uri = format!("api/v1/cas/{}/parents/{}", handle, parent); + let parent: ParentCaContact = self.get_json(&uri)?; + Ok(ApiResponse::ParentCaContact(parent)) + } + CaCommand::ChildInfo(handle, child) => { let uri = format!("api/v1/cas/{}/children/{}", handle, child); let info: ChildCaInfo = self.get_json(&uri)?; diff --git a/src/cli/options.rs b/src/cli/options.rs index 7c097f90..7ca97658 100644 --- a/src/cli/options.rs +++ b/src/cli/options.rs @@ -386,6 +386,17 @@ impl Options { app.subcommand(sub) } + fn make_cas_parents_contact_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + let mut sub = + SubCommand::with_name("contact").about("Show contact information for parent."); + + sub = Self::add_general_args(sub); + sub = Self::add_my_ca_arg(sub); + sub = Self::add_parent_arg(sub); + + app.subcommand(sub) + } + fn make_cas_parents_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { let mut sub = SubCommand::with_name("remove").about("Remove an existing parent from this CA."); @@ -403,6 +414,7 @@ impl Options { sub = Self::make_cas_parents_myid_sc(sub); sub = Self::make_cas_parents_add_sc(sub); sub = Self::make_cas_parents_update_sc(sub); + sub = Self::make_cas_parents_contact_sc(sub); sub = Self::make_cas_parents_remove_sc(sub); app.subcommand(sub) @@ -875,6 +887,16 @@ impl Options { } } + fn parse_matches_cas_parents_info(matches: &ArgMatches) -> Result { + let general_args = GeneralArgs::from_matches(matches)?; + let my_ca = Self::parse_my_ca(matches)?; + let parent = matches.value_of("parent").unwrap(); + let parent = Handle::from_str(parent).map_err(|_| Error::InvalidHandle)?; + + let command = Command::CertAuth(CaCommand::MyParentCaContact(my_ca, parent)); + Ok(Options::make(general_args, command)) + } + fn parse_matches_cas_parents_remove(matches: &ArgMatches) -> Result { let general_args = GeneralArgs::from_matches(matches)?; let my_ca = Self::parse_my_ca(matches)?; @@ -892,6 +914,8 @@ impl Options { Self::parse_matches_cas_parents_add(m) } else if let Some(m) = matches.subcommand_matches("update") { Self::parse_matches_cas_parents_update(m) + } else if let Some(m) = matches.subcommand_matches("contact") { + Self::parse_matches_cas_parents_info(m) } else if let Some(m) = matches.subcommand_matches("remove") { Self::parse_matches_cas_parents_remove(m) } else { @@ -1165,7 +1189,7 @@ pub enum CaCommand { // Update CA id UpdateId(Handle), - // Get an RFC8183 parent response + // Get an RFC8183 parent response for a child ParentResponse(Handle, ChildHandle), // Get the RFC8183 child request @@ -1178,6 +1202,8 @@ pub enum CaCommand { // Add a parent to this CA AddParent(Handle, ParentCaReq), + // Show my parent's contact + MyParentCaContact(Handle, ParentHandle), // Update parent contact UpdateParentContact(Handle, ParentHandle, ParentCaContact), diff --git a/src/cli/report.rs b/src/cli/report.rs index 2893ff08..4600b821 100644 --- a/src/cli/report.rs +++ b/src/cli/report.rs @@ -1,12 +1,12 @@ use std::str::{from_utf8_unchecked, FromStr}; use crate::commons::api::{ - CaRepoDetails, CertAuthHistory, CertAuthInfo, CertAuthList, CurrentObjects, CurrentRepoState, - ParentCaContact, PublisherDetails, PublisherList, RepositoryContact, RoaDefinition, + CaRepoDetails, CertAuthHistory, CertAuthInfo, CertAuthList, ChildCaInfo, CurrentObjects, + CurrentRepoState, ParentCaContact, PublisherDetails, PublisherList, RepositoryContact, + RoaDefinition, }; use crate::commons::remote::api::ClientInfo; use crate::commons::remote::rfc8183; -use commons::api::ChildCaInfo; //------------ ApiResponse --------------------------------------------------- @@ -162,10 +162,14 @@ impl Report for CertAuthInfo { } } - for (name, kind) in self.parents().iter() { - res.push_str(&format!("Parent: {}, Kind: {}\n", name, kind)); + res.push_str("Parents:\n"); + if !self.parents().is_empty() { + for parent in self.parents().iter() { + res.push_str(&format!("{}\n", parent)); + } + } else { + res.push_str("\n") } - res.push_str("\n"); for (name, rc) in self.resources() { res.push_str(&format!("Resource Class: {}\n", name,)); @@ -183,7 +187,7 @@ impl Report for CertAuthInfo { res.push_str(&format!("{}\n", child_handle)); } } else { - res.push_str(""); + res.push_str("\n"); } Ok(res) diff --git a/src/commons/api/ca.rs b/src/commons/api/ca.rs index db768fd0..c28d915f 100644 --- a/src/commons/api/ca.rs +++ b/src/commons/api/ca.rs @@ -19,12 +19,11 @@ use rpki::roa::Roa; use rpki::uri; use rpki::x509::{Serial, Time}; -use crate::commons::api::admin::{Handle, ParentCaContact}; use crate::commons::api::publication; use crate::commons::api::publication::Publish; use crate::commons::api::{ - Base64, ChildHandle, HexEncodedHash, IssuanceRequest, ListReply, ParentHandle, - RepositoryContact, RequestResourceLimit, RoaDefinition, + Base64, ChildHandle, Handle, HexEncodedHash, IssuanceRequest, ListReply, ParentCaContact, + ParentHandle, RepositoryContact, RequestResourceLimit, RoaDefinition, }; use crate::commons::eventsourcing::AggregateHistory; use crate::commons::remote::id::IdCert; @@ -1416,6 +1415,43 @@ impl CertAuthSummary { } } +//------------ ParentKindInfo ------------------------------------------------ +#[derive(Clone, Debug, Deserialize, Display, Eq, PartialEq, Serialize)] +pub enum ParentKindInfo { + #[display(fmt = "This CA is a TA")] + Ta, + + #[display(fmt = "Embedded parent")] + Embedded, + + #[display(fmt = "RFC 6492 Parent")] + Rfc6492, +} + +//------------ ParentInfo ---------------------------------------------------- +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ParentInfo { + handle: ParentHandle, + kind: ParentKindInfo, +} + +impl ParentInfo { + pub fn new(handle: ParentHandle, contact: ParentCaContact) -> Self { + let kind = match contact { + ParentCaContact::Ta(_) => ParentKindInfo::Ta, + ParentCaContact::Embedded => ParentKindInfo::Embedded, + ParentCaContact::Rfc6492(_) => ParentKindInfo::Rfc6492, + }; + ParentInfo { handle, kind } + } +} + +impl fmt::Display for ParentInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.handle, self.kind) + } +} + //------------ CertAuthInfo -------------------------------------------------- /// This type represents the details of a CertAuth that need @@ -1424,7 +1460,7 @@ impl CertAuthSummary { pub struct CertAuthInfo { handle: Handle, repo_info: RepoInfo, - parents: HashMap, + parents: Vec, resources: HashMap, children: Vec, roa_definitions: HashSet, @@ -1434,11 +1470,16 @@ impl CertAuthInfo { pub fn new( handle: Handle, repo_info: RepoInfo, - parents: HashMap, + parents: HashMap, resources: HashMap, children: Vec, roa_definitions: HashSet, ) -> Self { + let parents = parents + .into_iter() + .map(|(handle, contact)| ParentInfo::new(handle, contact)) + .collect(); + CertAuthInfo { handle, repo_info, @@ -1457,14 +1498,10 @@ impl CertAuthInfo { &self.repo_info } - pub fn parents(&self) -> &HashMap { + pub fn parents(&self) -> &Vec { &self.parents } - pub fn parent(&self, parent: &Handle) -> Option<&ParentCaContact> { - self.parents.get(parent) - } - pub fn resources(&self) -> &HashMap { &self.resources } diff --git a/src/daemon/endpoints.rs b/src/daemon/endpoints.rs index 2202c4c7..49c518c2 100644 --- a/src/daemon/endpoints.rs +++ b/src/daemon/endpoints.rs @@ -318,6 +318,20 @@ pub fn ca_info(server: web::Data, auth: Auth, handle: Path) - }) } +pub fn ca_my_parent_contact( + server: web::Data, + auth: Auth, + ca_and_parent: Path<(Handle, Handle)>, +) -> HttpResponse { + let (ca, parent) = ca_and_parent.into_inner(); + if_api_allowed(&server, &auth, || { + match server.read().ca_my_parent_contact(&ca, &parent) { + Some(info) => render_json(info), + None => api_not_found(), + } + }) +} + pub fn ca_history(server: web::Data, auth: Auth, handle: Path) -> HttpResponse { if_api_allowed(&server, &auth, || { match server.read().ca_history(&handle.into_inner()) { diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index f3f53fb8..8719005a 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -82,11 +82,12 @@ pub fn start(config: &Config) -> Result<(), Error> { .route("/cas/{ca}/repo/request", get().to(ca_publisher_req)) .route("/cas/{ca}/repo/", post().to(ca_repo_update)) .route("/cas/{ca}/parents", post().to(ca_add_parent)) + .route("/cas/{ca}/parents/{parent}", get().to(ca_my_parent_contact)) .route("/cas/{ca}/parents/{parent}", post().to(ca_update_parent)) .route("/cas/{ca}/parents/{parent}", delete().to(ca_remove_parent)) .route("/cas/{ca}/children", post().to(ca_add_child)) .route( - "/cas/{ca}/parent_contact/{child}", + "/cas/{ca}/children/{child}/contact", get().to(ca_parent_contact), ) .route("/cas/{ca}/children/{child}", get().to(ca_show_child)) diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index d1527fa4..a838f213 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -326,6 +326,21 @@ impl KrillServer { self.caserver.get_ca(handle).map(|ca| ca.as_ca_info()).ok() } + /// Returns the parent contact for a CA and parent, or NONE if either the CA or the parent cannot be found. + pub fn ca_my_parent_contact( + &self, + handle: &Handle, + parent: &ParentHandle, + ) -> Option { + match self.caserver.get_ca(handle) { + Err(_) => None, + Ok(ca) => match ca.parent(parent) { + Err(_) => None, + Ok(parent) => Some(parent.clone()), + }, + } + } + /// Returns the history for a CA, or NONE in case of issues (i.e. it does not exist). pub fn ca_history(&self, handle: &Handle) -> Option { self.caserver.get_ca_history(handle).ok()