use std::fmt;
use rpki::ca::idexchange;
use rpki::ca::idcert::IdCert;
use rpki::ca::idexchange::{
CaHandle, ChildHandle, ParentHandle, PublisherHandle, RepoInfo,
ServiceUri,
};
use rpki::ca::provisioning::ResourceClassName;
use rpki::ca::publication::Base64;
use rpki::crypto::PublicKey;
use rpki::repository::resources::ResourceSet;
use rpki::uri;
use serde::{Deserialize, Serialize};
use crate::commons::error::Error;
use crate::commons::KrillResult;
use super::ca::{IdCertInfo, Timestamp};
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Token(String);
impl From<&str> for Token {
fn from(s: &str) -> Self {
Token(s.to_string())
}
}
impl From<String> for Token {
fn from(s: String) -> Self {
Token(s)
}
}
impl AsRef<str> for Token {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublicationServerUris {
pub rrdp_base_uri: uri::Https,
pub rsync_jail: uri::Rsync,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublisherSummary {
pub handle: PublisherHandle,
}
impl PublisherSummary {
pub fn from_handle(handle: PublisherHandle) -> Self {
PublisherSummary { handle }
}
}
#[derive(Clone, Eq, Debug, Deserialize, PartialEq, Serialize)]
pub struct PublisherList {
pub publishers: Vec<PublisherSummary>,
}
impl PublisherList {
pub fn from_slice(publishers: &[PublisherHandle]) -> PublisherList {
PublisherList {
publishers: publishers.iter().map(|p| {
PublisherSummary::from_handle(p.clone())
}).collect(),
}
}
}
impl fmt::Display for PublisherList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Publishers: ")?;
let mut first = true;
for p in &self.publishers {
if !first {
write!(f, ", ")?;
} else {
first = false;
}
write!(f, "{}", p.handle.as_str())?;
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublisherDetails {
pub handle: PublisherHandle,
pub id_cert: IdCertInfo,
pub base_uri: uri::Rsync,
pub current_files: Vec<PublishedFile>,
}
impl fmt::Display for PublisherDetails {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "handle: {}", self.handle)?;
writeln!(f, "id: {}", self.id_cert.public_key.key_identifier())?;
writeln!(f, "base uri: {}", self.base_uri)?;
writeln!(f, "objects:")?;
for e in &self.current_files {
writeln!(f, " {}", e.uri)?;
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublishedFile {
pub uri: uri::Rsync,
pub base64: Base64,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublicationServerInfo {
pub public_key: PublicKey,
pub service_uri: ServiceUri,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ApiRepositoryContact {
pub repository_response: idexchange::RepositoryResponse,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RepositoryContact {
pub repo_info: RepoInfo,
pub server_info: PublicationServerInfo,
}
impl RepositoryContact {
pub fn try_from_response(
repository_response: idexchange::RepositoryResponse,
) -> KrillResult<Self> {
let id_cert = repository_response.validate().map_err(Error::rfc8183)?;
Ok(RepositoryContact {
repo_info: repository_response.repo_info().clone(),
server_info: PublicationServerInfo {
public_key: id_cert.public_key().clone(),
service_uri: repository_response.service_uri().clone(),
},
})
}
}
impl From<RepositoryContact> for RepoInfo {
fn from(contact: RepositoryContact) -> Self {
contact.repo_info
}
}
impl fmt::Display for RepositoryContact {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "publication server at {}", self.server_info.service_uri)
}
}
impl std::hash::Hash for RepositoryContact {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.server_info.service_uri.as_str().hash(state); }
}
impl PartialEq for RepositoryContact {
fn eq(&self, other: &Self) -> bool {
self.repo_info == other.repo_info
&& self.server_info == other.server_info
}
}
impl Eq for RepositoryContact {}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ParentCaReq {
pub handle: ParentHandle,
#[serde(alias = "contact")] pub response: idexchange::ParentResponse,
}
impl fmt::Display for ParentCaReq {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "parent '{}' contact '{}'", self.handle, self.response)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ParentServerInfo {
pub service_uri: ServiceUri,
pub parent_handle: ParentHandle,
pub child_handle: ChildHandle,
pub id_cert: IdCertInfo,
}
impl fmt::Display for ParentServerInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "service uri: {}", self.service_uri)?;
writeln!(f, "parent handle: {}", self.parent_handle)?;
writeln!(f, "child handle: {}", self.child_handle)?;
writeln!(f, "parent certificate:")?;
writeln!(
f,
" key identifier: {}",
self.id_cert.public_key.key_identifier()
)?;
writeln!(f, " hash (of cert): {}", self.id_cert.hash)?;
writeln!(f, " PEM:\n\n{}", self.id_cert.pem())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum ParentCaContact {
Rfc6492(ParentServerInfo),
}
impl ParentCaContact {
pub fn try_from_rfc8183_parent_response(
response: idexchange::ParentResponse,
) -> Result<Self, idexchange::Error> {
let id_cert = response.validate()?;
let id_cert = IdCertInfo::from(&id_cert);
let service_uri = response.service_uri().clone();
let parent_handle = response.parent_handle().clone();
let child_handle = response.child_handle().clone();
Ok(ParentCaContact::Rfc6492(ParentServerInfo {
service_uri,
parent_handle,
child_handle,
id_cert,
}))
}
pub fn parent_server_info(&self) -> &ParentServerInfo {
match &self {
ParentCaContact::Rfc6492(info) => info,
}
}
pub fn parent_uri(&self) -> &idexchange::ServiceUri {
match self {
ParentCaContact::Rfc6492(parent) => &parent.service_uri,
}
}
}
impl fmt::Display for ParentCaContact {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParentCaContact::Rfc6492(response) => response.fmt(f),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StorableParentContact {
Rfc6492,
}
impl fmt::Display for StorableParentContact {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
StorableParentContact::Rfc6492 => write!(f, "RFC 6492 Parent"),
}
}
}
impl From<ParentCaContact> for StorableParentContact {
fn from(parent: ParentCaContact) -> Self {
match parent {
ParentCaContact::Rfc6492(_) => StorableParentContact::Rfc6492,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct CertAuthInit {
pub handle: CaHandle,
}
impl fmt::Display for CertAuthInit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.handle)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct AddChildRequest {
pub handle: ChildHandle,
pub resources: ResourceSet,
pub id_cert: IdCert,
}
impl fmt::Display for AddChildRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "handle '{}' resources '{}'", self.handle, self.resources,)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct UpdateChildRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub id_cert: Option<IdCert>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resources: Option<ResourceSet>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suspend: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resource_class_name_mapping: Option<ResourceClassNameMapping>,
}
impl UpdateChildRequest {
pub fn id_cert(id_cert: IdCert) -> Self {
UpdateChildRequest {
id_cert: Some(id_cert),
resources: None,
suspend: None,
resource_class_name_mapping: None,
}
}
pub fn resources(resources: ResourceSet) -> Self {
UpdateChildRequest {
id_cert: None,
resources: Some(resources),
suspend: None,
resource_class_name_mapping: None,
}
}
pub fn suspend() -> Self {
UpdateChildRequest {
id_cert: None,
resources: None,
suspend: Some(true),
resource_class_name_mapping: None,
}
}
pub fn unsuspend() -> Self {
UpdateChildRequest {
id_cert: None,
resources: None,
suspend: Some(false),
resource_class_name_mapping: None,
}
}
pub fn resource_class_name_mapping(
mapping: ResourceClassNameMapping,
) -> Self {
UpdateChildRequest {
id_cert: None,
resources: None,
suspend: None,
resource_class_name_mapping: Some(mapping),
}
}
}
impl fmt::Display for UpdateChildRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.id_cert.is_some() {
write!(f, "new id cert ")?;
}
if let Some(resources) = &self.resources {
write!(f, "new resources: {resources} ")?;
}
if let Some(suspend) = self.suspend {
write!(f, "change suspend status to: {suspend}")?;
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ResourceClassNameMapping {
pub name_in_parent: ResourceClassName,
pub name_for_child: ResourceClassName,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ServerInfo {
pub version: String,
pub started: Timestamp,
}
impl fmt::Display for ServerInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Version: {}\nStarted: {}",
self.version,
self.started.into_rfc3339()
)
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct RepoFileDeleteCriteria {
pub base_uri: uri::Rsync,
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::str::FromStr;
use super::*;
#[test]
fn should_accept_rfc8183_handle() {
CaHandle::from_str("abcDEF012/\\-_").unwrap();
}
#[test]
fn should_reject_invalid_handle() {
assert!(CaHandle::from_str("&").is_err());
}
#[test]
fn should_make_file_system_safe() {
let handle = CaHandle::from_str("abcDEF012/\\-_").unwrap();
let expected_path_buf = PathBuf::from("abcDEF012+=-_");
assert_eq!(handle.to_path_buf(), expected_path_buf);
}
#[test]
fn should_make_handle_from_dir() {
let path = PathBuf::from("a/b/abcDEF012+=-_");
let handle = CaHandle::try_from(&path).unwrap();
let expected_handle = CaHandle::from_str("abcDEF012/\\-_").unwrap();
assert_eq!(handle, expected_handle);
}
}