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