1 //! Support for admin tasks, such as managing publishers and RFC8181 clients 2 3 use std::{ 4 convert::TryFrom, 5 fmt, 6 path::PathBuf, 7 str::{from_utf8_unchecked, FromStr}, 8 sync::Arc, 9 }; 10 11 use rfc8183::ServiceUri; 12 use serde::{ 13 de, {Deserialize, Deserializer, Serialize, Serializer}, 14 }; 15 16 use rpki::{repository::cert::Cert, uri}; 17 18 use crate::commons::{ 19 api::{ 20 ca::{ResourceSet, TrustAnchorLocator}, 21 rrdp::PublishElement, 22 RepoInfo, Timestamp, 23 }, 24 crypto::IdCert, 25 remote::rfc8183, 26 }; 27 28 //------------ Handle -------------------------------------------------------- 29 30 // Some type aliases that help make the use of Handles more explicit. 31 pub type ParentHandle = Handle; 32 pub type ChildHandle = Handle; 33 pub type PublisherHandle = Handle; 34 pub type RepositoryHandle = Handle; 35 36 #[derive(Clone, Debug, Eq, Hash, PartialEq)] 37 pub struct Handle { 38 name: Arc<str>, 39 } 40 41 impl Handle { as_str(&self) -> &str42 pub fn as_str(&self) -> &str { 43 self.as_ref() 44 } 45 46 /// We replace "/" with "+" and "\" with "=" to make file system 47 /// safe names. to_path_buf(&self) -> PathBuf48 pub fn to_path_buf(&self) -> PathBuf { 49 let s = self.to_string(); 50 let s = s.replace("/", "+"); 51 let s = s.replace("\\", "="); 52 PathBuf::from(s) 53 } 54 } 55 56 impl TryFrom<&PathBuf> for Handle { 57 type Error = InvalidHandle; 58 59 fn try_from(path: &PathBuf) -> Result<Self, Self::Error> { 60 if let Some(path) = path.file_name() { 61 let s = path.to_string_lossy().to_string(); 62 let s = s.replace("+", "/"); 63 let s = s.replace("=", "\\"); 64 Self::from_str(&s) 65 } else { 66 Err(InvalidHandle) 67 } 68 } 69 } 70 71 impl FromStr for Handle { 72 type Err = InvalidHandle; 73 74 /// Accepted pattern: [-_A-Za-z0-9/]{1,255} 75 /// See Appendix A of RFC8183. 76 /// 77 fn from_str(s: &str) -> Result<Self, Self::Err> { 78 if s.bytes() 79 .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'/' || b == b'\\') 80 && !s.is_empty() 81 && s.len() < 256 82 { 83 Ok(Handle { name: s.into() }) 84 } else { 85 Err(InvalidHandle) 86 } 87 } 88 } 89 90 impl AsRef<str> for Handle { 91 fn as_ref(&self) -> &str { 92 &self.name 93 } 94 } 95 96 impl AsRef<[u8]> for Handle { 97 fn as_ref(&self) -> &[u8] { 98 self.name.as_bytes() 99 } 100 } 101 102 impl fmt::Display for Handle { 103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 104 write!(f, "{}", self.as_str()) 105 } 106 } 107 108 impl Serialize for Handle { 109 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 110 where 111 S: Serializer, 112 { 113 self.to_string().serialize(serializer) 114 } 115 } 116 117 impl<'de> Deserialize<'de> for Handle { 118 fn deserialize<D>(deserializer: D) -> Result<Handle, D::Error> 119 where 120 D: Deserializer<'de>, 121 { 122 let string = String::deserialize(deserializer)?; 123 let handle = Handle::from_str(&string).map_err(de::Error::custom)?; 124 Ok(handle) 125 } 126 } 127 128 #[derive(Debug)] 129 pub struct InvalidHandle; 130 131 impl fmt::Display for InvalidHandle { 132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 133 write!(f, "Handle MUST have pattern: [-_A-Za-z0-9/]{{1,255}}") 134 } 135 } 136 137 //------------ Token ------------------------------------------------------ 138 139 #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] 140 pub struct Token(String); 141 142 impl From<&str> for Token { 143 fn from(s: &str) -> Self { 144 Token(s.to_string()) 145 } 146 } 147 148 impl From<String> for Token { 149 fn from(s: String) -> Self { 150 Token(s) 151 } 152 } 153 154 impl AsRef<str> for Token { 155 fn as_ref(&self) -> &str { 156 &self.0 157 } 158 } 159 160 impl fmt::Display for Token { 161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 162 self.0.fmt(f) 163 } 164 } 165 166 //------------ PublicationServerUris ----------------------------------------- 167 168 /// Contains the information needed to initialize a new Publication Server 169 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 170 pub struct PublicationServerUris { 171 rrdp_base_uri: uri::Https, 172 rsync_jail: uri::Rsync, 173 } 174 175 impl PublicationServerUris { 176 pub fn new(rrdp_base_uri: uri::Https, rsync_jail: uri::Rsync) -> Self { 177 PublicationServerUris { 178 rrdp_base_uri, 179 rsync_jail, 180 } 181 } 182 183 pub fn rrdp_base_uri(&self) -> &uri::Https { 184 &self.rrdp_base_uri 185 } 186 187 pub fn rsync_jail(&self) -> &uri::Rsync { 188 &self.rsync_jail 189 } 190 191 pub fn unpack(self) -> (uri::Https, uri::Rsync) { 192 (self.rrdp_base_uri, self.rsync_jail) 193 } 194 } 195 196 //------------ PublisherSummaryInfo ------------------------------------------ 197 198 /// Defines a summary of publisher information to be used in the publisher 199 /// list. 200 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 201 pub struct PublisherSummary { 202 handle: PublisherHandle, 203 } 204 205 impl PublisherSummary { 206 pub fn handle(&self) -> &PublisherHandle { 207 &self.handle 208 } 209 } 210 211 impl From<&Handle> for PublisherSummary { 212 fn from(h: &Handle) -> Self { 213 PublisherSummary { handle: h.clone() } 214 } 215 } 216 217 //------------ PublisherList ------------------------------------------------- 218 219 /// This type represents a list of (all) current publishers to show in the API 220 #[derive(Clone, Eq, Debug, Deserialize, PartialEq, Serialize)] 221 pub struct PublisherList { 222 publishers: Vec<PublisherSummary>, 223 } 224 225 impl PublisherList { 226 pub fn build(publishers: &[Handle]) -> PublisherList { 227 let publishers: Vec<PublisherSummary> = publishers.iter().map(|p| p.into()).collect(); 228 229 PublisherList { publishers } 230 } 231 232 pub fn publishers(&self) -> &Vec<PublisherSummary> { 233 &self.publishers 234 } 235 } 236 237 impl fmt::Display for PublisherList { 238 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 239 write!(f, "Publishers: ")?; 240 let mut first = true; 241 for p in self.publishers() { 242 if !first { 243 write!(f, ", ")?; 244 } else { 245 first = false; 246 } 247 write!(f, "{}", p.handle().as_str())?; 248 } 249 Ok(()) 250 } 251 } 252 253 //------------ PublisherDetails ---------------------------------------------- 254 255 /// This type defines the publisher details for: 256 /// /api/v1/publishers/{handle} 257 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 258 pub struct PublisherDetails { 259 handle: Handle, 260 id_cert: IdCert, 261 base_uri: uri::Rsync, 262 current_files: Vec<PublishElement>, 263 } 264 265 impl PublisherDetails { 266 pub fn new(handle: &Handle, id_cert: IdCert, base_uri: uri::Rsync, current_files: Vec<PublishElement>) -> Self { 267 PublisherDetails { 268 handle: handle.clone(), 269 id_cert, 270 base_uri, 271 current_files, 272 } 273 } 274 275 pub fn handle(&self) -> &Handle { 276 &self.handle 277 } 278 pub fn id_cert(&self) -> &IdCert { 279 &self.id_cert 280 } 281 pub fn base_uri(&self) -> &uri::Rsync { 282 &self.base_uri 283 } 284 pub fn current_files(&self) -> &Vec<PublishElement> { 285 &self.current_files 286 } 287 } 288 289 impl fmt::Display for PublisherDetails { 290 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 291 writeln!(f, "handle: {}", self.handle())?; 292 writeln!(f, "id: {}", self.id_cert().ski_hex())?; 293 writeln!(f, "base uri: {}", self.base_uri().to_string())?; 294 writeln!(f, "objects:")?; 295 for e in &self.current_files { 296 writeln!(f, " {}", e.uri())?; 297 } 298 299 Ok(()) 300 } 301 } 302 303 //------------ PubServerContact ---------------------------------------------- 304 305 #[derive(Clone, Debug, Deserialize, Serialize)] 306 #[allow(clippy::large_enum_variant)] 307 pub struct RepositoryContact { 308 repository_response: rfc8183::RepositoryResponse, 309 } 310 311 impl RepositoryContact { 312 pub fn new(repository_response: rfc8183::RepositoryResponse) -> Self { 313 RepositoryContact { repository_response } 314 } 315 316 pub fn uri(&self) -> String { 317 self.repository_response.service_uri().to_string() 318 } 319 320 pub fn response(&self) -> &rfc8183::RepositoryResponse { 321 &self.repository_response 322 } 323 324 pub fn repo_info(&self) -> &RepoInfo { 325 self.repository_response.repo_info() 326 } 327 328 pub fn service_uri(&self) -> &ServiceUri { 329 self.repository_response.service_uri() 330 } 331 } 332 333 impl fmt::Display for RepositoryContact { 334 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 335 write!( 336 f, 337 "remote publication server at {}", 338 self.repository_response.service_uri() 339 ) 340 } 341 } 342 343 impl std::hash::Hash for RepositoryContact { 344 fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 345 self.repository_response.to_string().hash(state) 346 } 347 } 348 349 impl std::cmp::PartialEq for RepositoryContact { 350 fn eq(&self, other: &Self) -> bool { 351 self.repository_response == other.repository_response 352 } 353 } 354 355 impl std::cmp::Eq for RepositoryContact {} 356 357 //------------ ParentCaReq --------------------------------------------------- 358 359 /// This type defines all parent ca details needed to add a parent to a CA 360 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 361 pub struct ParentCaReq { 362 handle: ParentHandle, // the local name the child gave to the parent 363 contact: ParentCaContact, // where the parent can be contacted 364 } 365 366 impl fmt::Display for ParentCaReq { 367 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 368 write!(f, "parent '{}' contact '{}'", self.handle, self.contact) 369 } 370 } 371 372 impl ParentCaReq { 373 pub fn new(handle: Handle, contact: ParentCaContact) -> Self { 374 ParentCaReq { handle, contact } 375 } 376 377 pub fn handle(&self) -> &ParentHandle { 378 &self.handle 379 } 380 381 pub fn contact(&self) -> &ParentCaContact { 382 &self.contact 383 } 384 385 pub fn unpack(self) -> (Handle, ParentCaContact) { 386 (self.handle, self.contact) 387 } 388 } 389 390 //------------ TaCertDetails ------------------------------------------------- 391 392 #[derive(Clone, Debug, Deserialize, Serialize)] 393 pub struct TaCertDetails { 394 cert: Cert, 395 resources: ResourceSet, 396 tal: TrustAnchorLocator, 397 } 398 399 impl TaCertDetails { 400 pub fn new(cert: Cert, resources: ResourceSet, tal: TrustAnchorLocator) -> Self { 401 TaCertDetails { cert, resources, tal } 402 } 403 404 pub fn cert(&self) -> &Cert { 405 &self.cert 406 } 407 408 pub fn resources(&self) -> &ResourceSet { 409 &self.resources 410 } 411 412 pub fn tal(&self) -> &TrustAnchorLocator { 413 &self.tal 414 } 415 } 416 417 impl PartialEq for TaCertDetails { 418 fn eq(&self, other: &Self) -> bool { 419 self.tal == other.tal 420 && self.resources == other.resources 421 && self.cert.to_captured().as_slice() == other.cert.to_captured().as_slice() 422 } 423 } 424 425 impl Eq for TaCertDetails {} 426 427 //------------ ParentCaContact ----------------------------------------------- 428 429 /// This type contains the information needed to contact the parent ca 430 /// for resource provisioning requests (RFC6492). 431 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 432 #[allow(clippy::large_enum_variant)] 433 #[serde(rename_all = "snake_case")] 434 #[serde(tag = "type")] 435 pub enum ParentCaContact { 436 Ta(TaCertDetails), 437 Rfc6492(rfc8183::ParentResponse), 438 } 439 440 impl ParentCaContact { 441 pub fn for_rfc6492(response: rfc8183::ParentResponse) -> Self { 442 ParentCaContact::Rfc6492(response) 443 } 444 445 pub fn for_ta(ta_cert_details: TaCertDetails) -> Self { 446 ParentCaContact::Ta(ta_cert_details) 447 } 448 449 pub fn parent_response(&self) -> Option<&rfc8183::ParentResponse> { 450 match &self { 451 ParentCaContact::Ta(_) => None, 452 ParentCaContact::Rfc6492(res) => Some(res), 453 } 454 } 455 456 pub fn to_ta_cert(&self) -> &Cert { 457 match &self { 458 ParentCaContact::Ta(details) => details.cert(), 459 _ => panic!("Not a TA parent"), 460 } 461 } 462 463 pub fn is_ta(&self) -> bool { 464 matches!(*self, ParentCaContact::Ta(_)) 465 } 466 467 pub fn parent_uri(&self) -> Option<&ServiceUri> { 468 match &self { 469 ParentCaContact::Ta(_) => None, 470 ParentCaContact::Rfc6492(parent) => Some(parent.service_uri()), 471 } 472 } 473 } 474 475 impl fmt::Display for ParentCaContact { 476 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 477 match self { 478 ParentCaContact::Ta(details) => write!(f, "{}", details.tal()), 479 ParentCaContact::Rfc6492(response) => { 480 let bytes = response.encode_vec(); 481 let xml = unsafe { from_utf8_unchecked(&bytes) }; 482 write!(f, "{}", xml) 483 } 484 } 485 } 486 } 487 488 /// This type is used when saving and presenting command history 489 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 490 #[serde(rename_all = "snake_case")] 491 pub enum StorableParentContact { 492 Ta, 493 Rfc6492, 494 } 495 496 impl fmt::Display for StorableParentContact { 497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 498 match self { 499 StorableParentContact::Ta => write!(f, "This CA is a TA"), 500 StorableParentContact::Rfc6492 => write!(f, "RFC 6492 Parent"), 501 } 502 } 503 } 504 505 impl From<ParentCaContact> for StorableParentContact { 506 fn from(parent: ParentCaContact) -> Self { 507 match parent { 508 ParentCaContact::Ta(_) => StorableParentContact::Ta, 509 ParentCaContact::Rfc6492(_) => StorableParentContact::Rfc6492, 510 } 511 } 512 } 513 514 //------------ CertAuthInit -------------------------------------------------- 515 516 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 517 pub struct CertAuthInit { 518 handle: Handle, 519 } 520 521 impl fmt::Display for CertAuthInit { 522 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 523 write!(f, "{}", self.handle) 524 } 525 } 526 527 impl CertAuthInit { 528 pub fn new(handle: Handle) -> Self { 529 CertAuthInit { handle } 530 } 531 532 pub fn unpack(self) -> Handle { 533 self.handle 534 } 535 } 536 537 //------------ AddChildRequest ----------------------------------------------- 538 539 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 540 pub struct AddChildRequest { 541 handle: Handle, 542 resources: ResourceSet, 543 id_cert: IdCert, 544 } 545 546 impl fmt::Display for AddChildRequest { 547 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 548 write!(f, "handle '{}' resources '{}'", self.handle, self.resources,) 549 } 550 } 551 552 impl AddChildRequest { 553 pub fn new(handle: Handle, resources: ResourceSet, id_cert: IdCert) -> Self { 554 AddChildRequest { 555 handle, 556 resources, 557 id_cert, 558 } 559 } 560 561 pub fn unpack(self) -> (Handle, ResourceSet, IdCert) { 562 (self.handle, self.resources, self.id_cert) 563 } 564 } 565 566 //------------ UpdateChildRequest -------------------------------------------- 567 568 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 569 pub struct UpdateChildRequest { 570 #[serde(skip_serializing_if = "Option::is_none")] 571 id_cert: Option<IdCert>, 572 573 #[serde(skip_serializing_if = "Option::is_none")] 574 resources: Option<ResourceSet>, 575 576 #[serde(skip_serializing_if = "Option::is_none")] 577 suspend: Option<bool>, 578 } 579 580 impl UpdateChildRequest { 581 pub fn new(id_cert: Option<IdCert>, resources: Option<ResourceSet>, suspend: Option<bool>) -> Self { 582 UpdateChildRequest { 583 id_cert, 584 resources, 585 suspend, 586 } 587 } 588 pub fn id_cert(id_cert: IdCert) -> Self { 589 UpdateChildRequest { 590 id_cert: Some(id_cert), 591 resources: None, 592 suspend: None, 593 } 594 } 595 596 pub fn resources(resources: ResourceSet) -> Self { 597 UpdateChildRequest { 598 id_cert: None, 599 resources: Some(resources), 600 suspend: None, 601 } 602 } 603 604 pub fn suspend() -> Self { 605 UpdateChildRequest { 606 id_cert: None, 607 resources: None, 608 suspend: Some(true), 609 } 610 } 611 612 pub fn unsuspend() -> Self { 613 UpdateChildRequest { 614 id_cert: None, 615 resources: None, 616 suspend: Some(false), 617 } 618 } 619 620 pub fn unpack(self) -> (Option<IdCert>, Option<ResourceSet>, Option<bool>) { 621 (self.id_cert, self.resources, self.suspend) 622 } 623 } 624 625 impl fmt::Display for UpdateChildRequest { 626 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 627 if self.id_cert.is_some() { 628 write!(f, "new id cert ")?; 629 } 630 if let Some(resources) = &self.resources { 631 write!(f, "new resources: {} ", resources)?; 632 } 633 if let Some(suspend) = self.suspend { 634 write!(f, "change suspend status to: {}", suspend)?; 635 } 636 Ok(()) 637 } 638 } 639 640 //------------ ServerInfo ---------------------------------------------------- 641 642 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 643 pub struct ServerInfo { 644 version: String, 645 started: Timestamp, 646 } 647 648 impl ServerInfo { 649 pub fn new(version: &str, started: Timestamp) -> Self { 650 ServerInfo { 651 version: version.to_string(), 652 started, 653 } 654 } 655 656 pub fn version(&self) -> &str { 657 &self.version 658 } 659 660 pub fn started(&self) -> Timestamp { 661 self.started 662 } 663 } 664 665 impl fmt::Display for ServerInfo { 666 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 667 write!(f, "Version: {}\nStarted: {}", self.version(), self.started.to_rfc3339()) 668 } 669 } 670 671 //------------ Tests --------------------------------------------------------- 672 673 #[cfg(test)] 674 mod tests { 675 676 use super::*; 677 678 #[test] 679 fn should_accept_rfc8183_handle() { 680 // See appendix A of RFC8183 681 // handle = xsd:string { maxLength="255" pattern="[\-_A-Za-z0-9/]*" } 682 Handle::from_str("abcDEF012/\\-_").unwrap(); 683 } 684 685 #[test] 686 fn should_reject_invalid_handle() { 687 // See appendix A of RFC8183 688 // handle = xsd:string { maxLength="255" pattern="[\-_A-Za-z0-9/]*" } 689 assert!(Handle::from_str("&").is_err()); 690 } 691 692 #[test] 693 fn should_make_file_system_safe() { 694 let handle = Handle::from_str("abcDEF012/\\-_").unwrap(); 695 let expected_path_buf = PathBuf::from("abcDEF012+=-_"); 696 assert_eq!(handle.to_path_buf(), expected_path_buf); 697 } 698 699 #[test] 700 fn should_make_handle_from_dir() { 701 let path = PathBuf::from("a/b/abcDEF012+=-_"); 702 let handle = Handle::try_from(&path).unwrap(); 703 let expected_handle = Handle::from_str("abcDEF012/\\-_").unwrap(); 704 assert_eq!(handle, expected_handle); 705 } 706 } 707