1 //! Hyper based HTTP server for Krill.
2 //!
3 use std::{
4     collections::HashMap,
5     convert::Infallible,
6     env,
7     fs::File,
8     io::Read,
9     path::{Path, PathBuf},
10     process,
11     str::FromStr,
12     sync::Arc,
13 };
14 
15 use bytes::Bytes;
16 use rpki::repository::resources::AsId;
17 use serde::Serialize;
18 
19 use futures::TryFutureExt;
20 use hyper::{
21     header::HeaderName,
22     http::HeaderValue,
23     server::conn::AddrIncoming,
24     service::{make_service_fn, service_fn},
25     Method,
26 };
27 
28 use crate::{
29     commons::{
30         api::{
31             AspaDefinitionUpdates, BgpStats, ChildHandle, CommandHistoryCriteria, Handle, ParentCaContact, ParentCaReq,
32             ParentHandle, PublisherList, RepositoryContact, RoaDefinitionUpdates, RtaName, Token,
33         },
34         bgp::BgpAnalysisAdvice,
35         error::Error,
36         eventsourcing::AggregateStoreError,
37         remote::rfc8183,
38         util::file,
39         KrillResult,
40     },
41     constants::{
42         KRILL_ENV_HTTP_LOG_INFO, KRILL_ENV_UPGRADE_ONLY, KRILL_VERSION_MAJOR, KRILL_VERSION_MINOR, KRILL_VERSION_PATCH,
43         NO_RESOURCE,
44     },
45     daemon::{
46         auth::common::permissions::Permission,
47         auth::Auth,
48         ca::{CaStatus, RouteAuthorizationUpdates, TA_NAME},
49         config::Config,
50         http::{
51             auth::auth, statics::statics, testbed::testbed, tls, tls_keys, HttpResponse, Request, RequestPath,
52             RoutingResult,
53         },
54         krillserver::KrillServer,
55     },
56     upgrades::{post_start_upgrade, pre_start_upgrade, update_storage_version},
57 };
58 
59 //------------ State -----------------------------------------------------
60 
61 pub type State = Arc<KrillServer>;
62 
parse_config() -> KrillResult<Config>63 pub fn parse_config() -> KrillResult<Config> {
64     Config::create().map_err(|e| Error::Custom(format!("Could not parse config: {}", e)))
65 }
66 
print_write_error_hint_and_die(error_msg: String)67 fn print_write_error_hint_and_die(error_msg: String) {
68     eprintln!("{}", error_msg);
69     eprintln!();
70     eprintln!("Hint: if you use systemd you may need to override the allowed ReadWritePaths,");
71     eprintln!("the easiest way may be by doing 'systemctl edit krill' and add a section like:");
72     eprintln!();
73     eprintln!("[Service]");
74     eprintln!("ReadWritePaths=/local/path1 /local/path2 ...");
75 }
76 
write_pid_file_or_die(config: &Config)77 fn write_pid_file_or_die(config: &Config) {
78     let pid_file = config.pid_file();
79     if let Err(e) = file::save(process::id().to_string().as_bytes(), &pid_file) {
80         print_write_error_hint_and_die(format!("Could not write PID file: {}", e));
81     }
82 }
83 
test_data_dir_or_die(config_item: &str, dir: &Path)84 fn test_data_dir_or_die(config_item: &str, dir: &Path) {
85     let test_file = dir.join("test");
86 
87     if let Err(e) = file::save(b"test", &test_file) {
88         print_write_error_hint_and_die(format!(
89             "Cannot write to dir '{}' for configuration setting '{}', Error: {}",
90             dir.to_string_lossy(),
91             config_item,
92             e
93         ));
94     } else if let Err(e) = file::delete_file(&test_file) {
95         print_write_error_hint_and_die(format!(
96             "Cannot delete test file '{}' in dir for configuration setting '{}', Error: {}",
97             test_file.to_string_lossy(),
98             config_item,
99             e
100         ));
101     }
102 }
103 
test_data_dirs_or_die(config: &Config)104 fn test_data_dirs_or_die(config: &Config) {
105     test_data_dir_or_die("data_dir", &config.data_dir);
106     if let Some(rfc8181_log_dir) = &config.rfc8181_log_dir {
107         test_data_dir_or_die("rfc8181_log_dir", rfc8181_log_dir);
108     }
109     if let Some(rfc6492_log_dir) = &config.rfc6492_log_dir {
110         test_data_dir_or_die("rfc6492_log_dir", rfc6492_log_dir);
111     }
112 }
113 
start_krill_daemon(config: Arc<Config>) -> Result<(), Error>114 pub async fn start_krill_daemon(config: Arc<Config>) -> Result<(), Error> {
115     write_pid_file_or_die(&config);
116     test_data_dirs_or_die(&config);
117 
118     // Call upgrade, this will only do actual work if needed.
119     pre_start_upgrade(config.clone())?;
120 
121     // Create the server, this will create the necessary data sub-directories if needed
122     let krill = KrillServer::build(config.clone()).await?;
123 
124     // Call post-start upgrades to trigger any upgrade related runtime actions, such as
125     // re-issuing ROAs because subject name strategy has changed.
126     post_start_upgrade(&config, &krill).await?;
127 
128     // Update the version identifiers for the storage dirs
129     update_storage_version(&config.data_dir).await?;
130 
131     // If the operator wanted to do the upgrade only, now is a good time to report success and stop
132     if env::var(KRILL_ENV_UPGRADE_ONLY).is_ok() {
133         println!("Krill upgrade successful");
134     }
135 
136     // Reset the RRDP session after a restart.
137     krill.repository_session_reset()?;
138 
139     let state = Arc::new(krill);
140 
141     let service = make_service_fn(move |_| {
142         let state = state.clone();
143         async move {
144             Ok::<_, Infallible>(service_fn(move |req: hyper::Request<hyper::Body>| {
145                 let state = state.clone();
146                 map_requests(req, state)
147             }))
148         }
149     });
150 
151     tls_keys::create_key_cert_if_needed(&config.data_dir).map_err(|e| Error::HttpsSetup(format!("{}", e)))?;
152 
153     let server_config_builder = tls::TlsConfigBuilder::new()
154         .cert_path(tls_keys::cert_file_path(&config.data_dir))
155         .key_path(tls_keys::key_file_path(&config.data_dir));
156     let server_config = server_config_builder.build().unwrap();
157 
158     let incoming = AddrIncoming::bind(&config.socket_addr()).map_err(|e| {
159         Error::Custom(format!(
160             "Could not bind to address and port: {}, Error: {}",
161             &config.socket_addr(),
162             e
163         ))
164     })?;
165 
166     let acceptor = tls::TlsAcceptor::new(server_config, incoming);
167 
168     let server = hyper::Server::builder(acceptor)
169         .serve(service)
170         .map_err(|e| eprintln!("Server error: {}", e));
171 
172     if server.await.is_err() {
173         eprintln!("Krill failed to start");
174     }
175 
176     Ok(())
177 }
178 
179 struct RequestLogger {
180     req_method: hyper::Method,
181     req_path: String,
182 }
183 
184 impl RequestLogger {
begin(req: &hyper::Request<hyper::Body>) -> Self185     fn begin(req: &hyper::Request<hyper::Body>) -> Self {
186         let req_method = req.method().clone();
187         let req_path = RequestPath::from_request(req).full().to_string();
188 
189         if log_enabled!(log::Level::Trace) {
190             trace!(
191                 "Request: method={} path={} headers={:?}",
192                 &req_method,
193                 &req_path,
194                 &req.headers()
195             );
196         }
197 
198         RequestLogger { req_method, req_path }
199     }
200 
end(&self, res: Result<&HttpResponse, &Error>)201     fn end(&self, res: Result<&HttpResponse, &Error>) {
202         match res {
203             Ok(response) => {
204                 match (response.status(), response.benign(), response.cause()) {
205                     (s, false, Some(cause)) if s.is_client_error() => warn!("HTTP {}: {}", s.as_u16(), cause),
206                     (s, false, Some(cause)) if s.is_server_error() => error!("HTTP {}: {}", s.as_u16(), cause),
207                     _ => {}
208                 }
209 
210                 if env::var(KRILL_ENV_HTTP_LOG_INFO).is_ok() {
211                     info!("{} {} {}", self.req_method, self.req_path, response.status());
212                 } else {
213                     debug!("{} {} {}", self.req_method, self.req_path, response.status());
214                 }
215                 if response.loggable() && log_enabled!(log::Level::Trace) {
216                     trace!("Response: headers={:?} body={:?}", response.headers(), response.body());
217                 }
218             }
219             Err(err) => {
220                 error!("{} {} Error: {}", self.req_method, self.req_path, err);
221             }
222         }
223     }
224 }
225 
map_requests(req: hyper::Request<hyper::Body>, state: State) -> Result<hyper::Response<hyper::Body>, Error>226 async fn map_requests(req: hyper::Request<hyper::Body>, state: State) -> Result<hyper::Response<hyper::Body>, Error> {
227     let logger = RequestLogger::begin(&req);
228 
229     let req = Request::new(req, state).await;
230 
231     // Save any updated auth details, e.g. if an OpenID Connect token needed
232     // refreshing.
233     let new_auth = req.actor().new_auth();
234 
235     // We used to use .or_else() here but that causes a large recursive call
236     // tree due to these calls being to async functions, large enough with the
237     // given Request object passed each time that it eventually resulted in
238     // stack overflow. By doing it by hand like this we avoid the use of the
239     // macros that cause the recursion. We could also look at putting less data
240     // on the stack.
241     let mut res = api(req).await;
242     if let Err(req) = res {
243         res = auth(req).await;
244     }
245     if let Err(req) = res {
246         res = health(req).await;
247     }
248     if let Err(req) = res {
249         res = metrics(req).await;
250     }
251     if let Err(req) = res {
252         res = stats(req).await;
253     }
254     if let Err(req) = res {
255         res = rfc8181(req).await;
256     }
257     if let Err(req) = res {
258         res = rfc6492(req).await;
259     }
260     if let Err(req) = res {
261         res = statics(req).await;
262     }
263     if let Err(req) = res {
264         res = ta(req).await;
265     }
266     if let Err(req) = res {
267         res = rrdp(req).await;
268     }
269     if let Err(req) = res {
270         res = testbed(req).await;
271     }
272     if let Err(req) = res {
273         res = render_not_found(req).await;
274     }
275 
276     let res = res.map_err(|_| Error::custom("should have received not found response"));
277 
278     // Augment the response with any updated auth details that were determined
279     // above.
280     let res = add_new_auth_to_response(res, new_auth);
281 
282     // Log the request and the response.
283     logger.end(res.as_ref());
284 
285     res.map(|res| res.response())
286 }
287 
288 //------------ Support Functions ---------------------------------------------
289 
290 /// HTTP redirects cannot have a response body and so we cannot render the error
291 /// to be displayed in Lagosta as a JSON body, instead we must package the JSON
292 /// as a query parameter.
render_error_redirect(err: Error) -> RoutingResult293 pub fn render_error_redirect(err: Error) -> RoutingResult {
294     let response = err.to_error_response();
295     let json = serde_json::to_string(&response).or_else(|err| {
296         Ok(format!(
297             "JSON serialization error while processing internal error: {}",
298             err
299         ))
300     })?;
301     let b64 = base64::encode(json);
302     let location = format!("/index.html#/login?error={}", b64);
303     Ok(HttpResponse::found(&location))
304 }
305 
render_empty_res(res: Result<(), Error>) -> RoutingResult306 pub fn render_empty_res(res: Result<(), Error>) -> RoutingResult {
307     match res {
308         Ok(()) => render_ok(),
309         Err(e) => render_error(e),
310     }
311 }
312 
313 #[allow(clippy::unnecessary_wraps)]
render_error(e: Error) -> RoutingResult314 fn render_error(e: Error) -> RoutingResult {
315     debug!("Server Error: {}", e);
316     Ok(HttpResponse::response_from_error(e))
317 }
318 
319 #[allow(clippy::unnecessary_wraps)]
render_json<O: Serialize>(obj: O) -> RoutingResult320 fn render_json<O: Serialize>(obj: O) -> RoutingResult {
321     Ok(HttpResponse::json(&obj))
322 }
323 
render_json_res<O: Serialize>(res: Result<O, Error>) -> RoutingResult324 fn render_json_res<O: Serialize>(res: Result<O, Error>) -> RoutingResult {
325     match res {
326         Ok(o) => render_json(o),
327         Err(e) => render_error(e),
328     }
329 }
330 
331 /// A clean 404 result for the API (no content, not for humans)
332 #[allow(clippy::unnecessary_wraps)]
render_unknown_resource() -> RoutingResult333 fn render_unknown_resource() -> RoutingResult {
334     Ok(HttpResponse::response_from_error(Error::ApiUnknownResource))
335 }
336 
337 /// A clean 200 result for the API (no content, not for humans)
338 #[allow(clippy::unnecessary_wraps)]
render_ok() -> RoutingResult339 pub fn render_ok() -> RoutingResult {
340     Ok(HttpResponse::ok())
341 }
342 
343 #[allow(clippy::unnecessary_wraps)]
render_unknown_method() -> RoutingResult344 pub fn render_unknown_method() -> RoutingResult {
345     Ok(HttpResponse::response_from_error(Error::ApiUnknownMethod))
346 }
347 
348 /// A clean 404 response
349 #[allow(clippy::unnecessary_wraps)]
render_not_found(_req: Request) -> RoutingResult350 pub async fn render_not_found(_req: Request) -> RoutingResult {
351     Ok(HttpResponse::not_found())
352 }
353 
354 /// Returns the server health.
health(req: Request) -> RoutingResult355 pub async fn health(req: Request) -> RoutingResult {
356     if req.is_get() && req.path().segment() == "health" {
357         render_ok()
358     } else {
359         Err(req)
360     }
361 }
362 
363 /// Produce prometheus style metrics
metrics(req: Request) -> RoutingResult364 pub async fn metrics(req: Request) -> RoutingResult {
365     if req.is_get() && req.path().segment().starts_with("metrics") {
366         let server = req.state();
367 
368         struct AllBgpStats {
369             announcements_valid: HashMap<Handle, usize>,
370             announcements_invalid_asn: HashMap<Handle, usize>,
371             announcements_invalid_length: HashMap<Handle, usize>,
372             announcements_not_found: HashMap<Handle, usize>,
373             roas_too_permissive: HashMap<Handle, usize>,
374             roas_redundant: HashMap<Handle, usize>,
375             roas_stale: HashMap<Handle, usize>,
376             roas_total: HashMap<Handle, usize>,
377         }
378 
379         impl AllBgpStats {
380             fn add_ca(&mut self, ca: &Handle, stats: &BgpStats) {
381                 self.announcements_valid.insert(ca.clone(), stats.announcements_valid);
382                 self.announcements_invalid_asn
383                     .insert(ca.clone(), stats.announcements_invalid_asn);
384                 self.announcements_invalid_length
385                     .insert(ca.clone(), stats.announcements_invalid_length);
386                 self.announcements_not_found
387                     .insert(ca.clone(), stats.announcements_not_found);
388                 self.roas_too_permissive.insert(ca.clone(), stats.roas_too_permissive);
389                 self.roas_redundant.insert(ca.clone(), stats.roas_redundant);
390                 self.roas_stale.insert(ca.clone(), stats.roas_stale);
391                 self.roas_total.insert(ca.clone(), stats.roas_total);
392             }
393         }
394 
395         let mut res = String::new();
396 
397         let info = server.server_info();
398         res.push_str("# HELP krill_server_start unix timestamp in seconds of last krill server start\n");
399         res.push_str("# TYPE krill_server_start gauge\n");
400         res.push_str(&format!("krill_server_start {}\n", info.started()));
401         res.push('\n');
402 
403         res.push_str("# HELP krill_version_major krill server major version number\n");
404         res.push_str("# TYPE krill_version_major gauge\n");
405         res.push_str(&format!("krill_version_major {}\n", KRILL_VERSION_MAJOR));
406         res.push('\n');
407 
408         res.push_str("# HELP krill_version_minor krill server minor version number\n");
409         res.push_str("# TYPE krill_version_minor gauge\n");
410         res.push_str(&format!("krill_version_minor {}\n", KRILL_VERSION_MINOR));
411         res.push('\n');
412 
413         res.push_str("# HELP krill_version_patch krill server patch version number\n");
414         res.push_str("# TYPE krill_version_patch gauge\n");
415         res.push_str(&format!("krill_version_patch {}\n", KRILL_VERSION_PATCH));
416 
417         #[cfg(feature = "multi-user")]
418         {
419             res.push('\n');
420             res.push_str("# HELP krill_auth_session_cache_size total number of cached login session tokens\n");
421             res.push_str("# TYPE krill_auth_session_cache_size gauge\n");
422             res.push_str(&format!(
423                 "krill_auth_session_cache_size {}\n",
424                 server.login_session_cache_size()
425             ));
426         }
427 
428         if let Ok(cas_stats) = server.cas_stats().await {
429             let number_cas = cas_stats.len();
430 
431             res.push('\n');
432             res.push_str("# HELP krill_cas number of cas in krill\n");
433             res.push_str("# TYPE krill_cas gauge\n");
434             res.push_str(&format!("krill_cas {}\n", number_cas));
435 
436             if !server.config.metrics.metrics_hide_ca_details {
437                 // Show per CA details
438 
439                 let mut ca_status_map: HashMap<Handle, Arc<CaStatus>> = HashMap::new();
440 
441                 for ca in cas_stats.keys() {
442                     if let Ok(ca_status) = server.ca_status(ca).await {
443                         ca_status_map.insert(ca.clone(), ca_status);
444                     }
445                 }
446 
447                 {
448                     // CA -> Parent metrics
449 
450                     // krill_ca_parent_success{{ca="ca", parent="parent"}} 1
451                     // krill_ca_parent_last_success_time{{ca="ca", parent="parent"}} 1630921599 // timestamp
452 
453                     res.push('\n');
454                     res.push_str(
455                         "# HELP krill_ca_parent_success status of last CA to parent connection (0=issue, 1=success)\n",
456                     );
457                     res.push_str("# TYPE krill_ca_parent_success gauge\n");
458                     for (ca, status) in ca_status_map.iter() {
459                         if ca.as_str() != TA_NAME {
460                             for (parent, status) in status.parents().iter() {
461                                 // skip the ones for which we have no status yet, i.e it was really only just added
462                                 // and no attempt to connect has yet been made.
463                                 if let Some(exchange) = status.last_exchange() {
464                                     let value = if exchange.was_success() { 1 } else { 0 };
465                                     res.push_str(&format!(
466                                         "krill_ca_parent_success{{ca=\"{}\", parent=\"{}\"}} {}\n",
467                                         ca, parent, value
468                                     ));
469                                 }
470                             }
471                         }
472                     }
473 
474                     res.push('\n');
475                     res.push_str(
476                     "# HELP krill_ca_parent_last_success_time unix timestamp in seconds of last successful CA to parent connection\n",
477                 );
478                     res.push_str("# TYPE krill_ca_parent_last_success_time gauge\n");
479 
480                     for (ca, status) in ca_status_map.iter() {
481                         if ca.as_str() != TA_NAME {
482                             for (parent, status) in status.parents().iter() {
483                                 // skip the ones for which we have no successful connection at all. Most likely
484                                 // they were just added (in which case it will come) - or were never successful
485                                 // in which case the metric above will say that the status is 0
486                                 if let Some(last_success) = status.last_success() {
487                                     res.push_str(&format!(
488                                         "krill_ca_parent_last_success_time{{ca=\"{}\", parent=\"{}\"}} {}\n",
489                                         ca, parent, last_success
490                                     ));
491                                 }
492                             }
493                         }
494                     }
495                 }
496 
497                 {
498                     // CA -> Publication Server status
499 
500                     // krill_ca_repo_success{{ca="ca"}} 1
501                     // krill_ca_repo_last_success_time{{ca="ca"}} 1630921599
502                     // krill_ca_repo_next_before_time{{ca="ca"}} 1630921599
503 
504                     res.push('\n');
505                     res.push_str("# HELP krill_ca_ps_success status of last CA to Publication Server connection (0=issue, 1=success)\n");
506                     res.push_str("# TYPE krill_ca_ps_success gauge\n");
507                     for (ca, status) in ca_status_map.iter() {
508                         // skip the ones for which we have no status yet, i.e it was really only just added
509                         // and no attempt to connect has yet been made.
510                         if let Some(exchange) = status.repo().last_exchange() {
511                             let value = if exchange.was_success() { 1 } else { 0 };
512                             res.push_str(&format!("krill_ca_ps_success{{ca=\"{}\"}} {}\n", ca, value));
513                         }
514                     }
515 
516                     res.push('\n');
517                     res.push_str("# HELP krill_ca_ps_last_success_time unix timestamp in seconds of last successful CA to Publication Server connection\n");
518                     res.push_str("# TYPE krill_ca_ps_last_success_time gauge\n");
519                     for (ca, status) in ca_status_map.iter() {
520                         // skip the ones for which we have no status yet, i.e it was really only just added
521                         // and no attempt to connect has yet been made.
522                         if let Some(last_success) = status.repo().last_success() {
523                             res.push_str(&format!(
524                                 "krill_ca_ps_last_success_time{{ca=\"{}\"}} {}\n",
525                                 ca, last_success
526                             ));
527                         }
528                     }
529 
530                     res.push('\n');
531                     res.push_str("# HELP krill_ca_ps_next_planned_time unix timestamp in seconds of next planned CA to Publication Server connection (unless e.g. ROAs are changed)\n");
532                     res.push_str("# TYPE krill_ca_ps_next_planned_time gauge\n");
533                     for (ca, status) in ca_status_map.iter() {
534                         // skip the ones for which we have no status yet, i.e it was really only just added
535                         // and no attempt to connect has yet been made.
536                         let timestamp = status.repo().next_exchange_before();
537                         res.push_str(&format!(
538                             "krill_ca_ps_next_planned_time{{ca=\"{}\"}} {}\n",
539                             ca, timestamp
540                         ));
541                     }
542                 }
543 
544                 // Do not show child metrics if none of the CAs has any children..
545                 // Many users do not delegate so, showing these metrics would just be confusing.
546                 let any_children = cas_stats.values().any(|ca| ca.child_count() > 0);
547 
548                 if any_children && !server.config.metrics.metrics_hide_child_details {
549                     // CA -> Children
550 
551                     // krill_cas_children{ca="parent"} 11 // nr of children
552                     // krill_ca_child_success{ca="parent", child="child"} 1
553                     // krill_ca_child_state{ca="parent", child="child"} 1
554                     // krill_ca_child_last_connection{ca="parent", child="child"} 1630921599
555                     // krill_ca_child_last_success{ca="parent", child="child"} 1630921599
556                     // krill_ca_child_agent_total{ca="parent", ua="krill/0.9.2"} 11
557 
558                     res.push('\n');
559                     res.push_str("# HELP krill_cas_children number of children for CA\n");
560                     res.push_str("# TYPE krill_cas_children gauge\n");
561                     for (ca, status) in cas_stats.iter() {
562                         res.push_str(&format!(
563                             "krill_cas_children{{ca=\"{}\"}} {}\n",
564                             ca,
565                             status.child_count()
566                         ));
567                     }
568 
569                     res.push('\n');
570                     res.push_str(
571                         "# HELP krill_ca_child_success status of last child to CA connection (0=issue, 1=success)\n",
572                     );
573                     res.push_str("# TYPE krill_ca_child_success gauge\n");
574                     for (ca, status) in ca_status_map.iter() {
575                         // skip the ones for which we have no status yet, i.e it was really only just added
576                         // and no attempt to connect has yet been made.
577                         for (child, status) in status.children().iter() {
578                             if let Some(exchange) = status.last_exchange() {
579                                 let value = if exchange.was_success() { 1 } else { 0 };
580                                 res.push_str(&format!(
581                                     "krill_ca_child_success{{ca=\"{}\", child=\"{}\"}} {}\n",
582                                     ca, child, value
583                                 ));
584                             }
585                         }
586                     }
587 
588                     res.push('\n');
589                     res.push_str(
590                         "# HELP krill_ca_child_state child state (see 'suspend_child_after_inactive_hours' config) (0=suspended, 1=active)\n",
591                     );
592                     res.push_str("# TYPE krill_ca_child_state gauge\n");
593                     for (ca, status) in ca_status_map.iter() {
594                         for (child, status) in status.children().iter() {
595                             let value = if status.suspended().is_none() { 1 } else { 0 };
596 
597                             res.push_str(&format!(
598                                 "krill_ca_child_state{{ca=\"{}\", child=\"{}\"}} {}\n",
599                                 ca, child, value
600                             ));
601                         }
602                     }
603 
604                     res.push('\n');
605                     res.push_str("# HELP krill_ca_child_last_connection unix timestamp in seconds of last child to CA connection\n");
606                     res.push_str("# TYPE krill_ca_child_last_connection gauge\n");
607                     for (ca, status) in ca_status_map.iter() {
608                         // skip the ones for which we have no status yet, i.e it was really only just added
609                         // and no attempt to connect has yet been made.
610                         for (child, status) in status.children().iter() {
611                             if let Some(exchange) = status.last_exchange() {
612                                 let timestamp = exchange.timestamp();
613                                 res.push_str(&format!(
614                                     "krill_ca_child_last_connection{{ca=\"{}\", child=\"{}\"}} {}\n",
615                                     ca, child, timestamp
616                                 ));
617                             }
618                         }
619                     }
620 
621                     res.push('\n');
622                     res.push_str(
623                     "# HELP krill_ca_child_last_success unix timestamp in seconds of last successful child to CA connection\n",
624                 );
625                     res.push_str("# TYPE krill_ca_child_last_success gauge\n");
626                     for (ca, status) in ca_status_map.iter() {
627                         // skip the ones for which we have no status yet, i.e it was really only just added
628                         // and no attempt to connect has yet been made.
629                         for (child, status) in status.children().iter() {
630                             if let Some(time) = status.last_success() {
631                                 res.push_str(&format!(
632                                     "krill_ca_child_last_success{{ca=\"{}\", child=\"{}\"}} {}\n",
633                                     ca, child, time
634                                 ));
635                             }
636                         }
637                     }
638 
639                     res.push('\n');
640                     res.push_str(
641                     "# HELP krill_ca_child_agent_total total children per user agent based on their last connection\n",
642                 );
643                     res.push_str("# TYPE krill_ca_child_agent_total gauge\n");
644                     for (ca, status) in ca_status_map.iter() {
645                         // skip the ones for which we have no status yet, i.e it was really only just added
646                         // and no attempt to connect has yet been made.
647 
648                         let mut user_agent_totals: HashMap<String, usize> = HashMap::new();
649                         for status in status.children().values() {
650                             if let Some(exchange) = status.last_exchange() {
651                                 let agent = exchange.user_agent().cloned().unwrap_or_else(|| "<none>".to_string());
652                                 *user_agent_totals.entry(agent).or_insert(0) += 1;
653                             }
654                         }
655 
656                         for (ua, total) in user_agent_totals.iter() {
657                             res.push_str(&format!(
658                                 "krill_ca_child_agent_total{{ca=\"{}\", user_agent=\"{}\"}} {}\n",
659                                 ca, ua, total
660                             ));
661                         }
662                     }
663                 }
664 
665                 if !server.config.metrics.metrics_hide_roa_details {
666                     // BGP Announcement metrics
667 
668                     // Aggregate ROA vs BGP stats per status
669                     let mut all_bgp_stats = AllBgpStats {
670                         announcements_valid: HashMap::new(),
671                         announcements_invalid_asn: HashMap::new(),
672                         announcements_invalid_length: HashMap::new(),
673                         announcements_not_found: HashMap::new(),
674                         roas_too_permissive: HashMap::new(),
675                         roas_redundant: HashMap::new(),
676                         roas_stale: HashMap::new(),
677                         roas_total: HashMap::new(),
678                     };
679 
680                     for (ca, ca_stats) in cas_stats.iter() {
681                         all_bgp_stats.add_ca(ca, ca_stats.bgp_stats());
682                     }
683 
684                     res.push('\n');
685                     res.push_str("# HELP krill_cas_bgp_announcements_valid number of announcements seen for CA resources with RPKI state VALID\n");
686                     res.push_str("# TYPE krill_cas_bgp_announcements_valid gauge\n");
687                     for (ca, nr) in all_bgp_stats.announcements_valid.iter() {
688                         res.push_str(&format!("krill_cas_bgp_announcements_valid{{ca=\"{}\"}} {}\n", ca, nr));
689                     }
690 
691                     res.push('\n');
692                     res.push_str("# HELP krill_cas_bgp_announcements_invalid_asn number of announcements seen for CA resources with RPKI state INVALID (ASN mismatch)\n");
693                     res.push_str("# TYPE krill_cas_bgp_announcements_invalid_asn gauge\n");
694                     for (ca, nr) in all_bgp_stats.announcements_invalid_asn.iter() {
695                         res.push_str(&format!(
696                             "krill_cas_bgp_announcements_invalid_asn{{ca=\"{}\"}} {}\n",
697                             ca, nr
698                         ));
699                     }
700 
701                     res.push('\n');
702                     res.push_str("# HELP krill_cas_bgp_announcements_invalid_length number of announcements seen for CA resources with RPKI state INVALID (prefix exceeds max length)\n");
703                     res.push_str("# TYPE krill_cas_bgp_announcements_invalid_length gauge\n");
704                     for (ca, nr) in all_bgp_stats.announcements_invalid_length.iter() {
705                         res.push_str(&format!(
706                             "krill_cas_bgp_announcements_invalid_length{{ca=\"{}\"}} {}\n",
707                             ca, nr
708                         ));
709                     }
710 
711                     res.push('\n');
712                     res.push_str("# HELP krill_cas_bgp_announcements_not_found number of announcements seen for CA resources with RPKI state NOT FOUND (none of the CA's ROAs cover this)\n");
713                     res.push_str("# TYPE krill_cas_bgp_announcements_not_found gauge\n");
714                     for (ca, nr) in all_bgp_stats.announcements_not_found.iter() {
715                         res.push_str(&format!(
716                             "krill_cas_bgp_announcements_not_found{{ca=\"{}\"}} {}\n",
717                             ca, nr
718                         ));
719                     }
720 
721                     res.push('\n');
722                     res.push_str("# HELP krill_cas_bgp_roas_too_permissive number of ROAs for this CA which allow excess announcements (0 may also indicate that no BGP info is available)\n");
723                     res.push_str("# TYPE krill_cas_bgp_roas_too_permissive gauge\n");
724                     for (ca, nr) in all_bgp_stats.roas_too_permissive.iter() {
725                         res.push_str(&format!("krill_cas_bgp_roas_too_permissive{{ca=\"{}\"}} {}\n", ca, nr));
726                     }
727 
728                     res.push('\n');
729                     res.push_str("# HELP krill_cas_bgp_roas_redundant number of ROAs for this CA which are redundant (0 may also indicate that no BGP info is available)\n");
730                     res.push_str("# TYPE krill_cas_bgp_roas_redundant gauge\n");
731                     for (ca, nr) in all_bgp_stats.roas_redundant.iter() {
732                         res.push_str(&format!("krill_cas_bgp_roas_redundant{{ca=\"{}\"}} {}\n", ca, nr));
733                     }
734 
735                     res.push('\n');
736                     res.push_str("# HELP krill_cas_bgp_roas_stale number of ROAs for this CA for which no announcements are seen (0 may also indicate that no BGP info is available)\n");
737                     res.push_str("# TYPE krill_cas_bgp_roas_stale gauge\n");
738                     for (ca, nr) in all_bgp_stats.roas_stale.iter() {
739                         res.push_str(&format!("krill_cas_bgp_roas_stale{{ca=\"{}\"}} {}\n", ca, nr));
740                     }
741 
742                     res.push('\n');
743                     res.push_str("# HELP krill_cas_bgp_roas_total total number of ROAs for this CA\n");
744                     res.push_str("# TYPE krill_cas_bgp_roas_stale gauge\n");
745                     for (ca, nr) in all_bgp_stats.roas_total.iter() {
746                         res.push_str(&format!("krill_cas_bgp_roas_total{{ca=\"{}\"}} {}\n", ca, nr));
747                     }
748                 }
749             }
750         }
751 
752         if let Ok(stats) = server.repo_stats() {
753             let publishers = stats.get_publishers();
754 
755             res.push('\n');
756             res.push_str("# HELP krill_repo_publisher number of publishers in repository\n");
757             res.push_str("# TYPE krill_repo_publisher gauge\n");
758             res.push_str(&format!("krill_repo_publisher {}\n", publishers.len()));
759 
760             if let Some(last_update) = stats.last_update() {
761                 res.push('\n');
762                 res.push_str(
763                     "# HELP krill_repo_rrdp_last_update unix timestamp in seconds of last update by any publisher\n",
764                 );
765                 res.push_str("# TYPE krill_repo_rrdp_last_update gauge\n");
766                 res.push_str(&format!("krill_repo_rrdp_last_update {}\n", last_update.timestamp()));
767             }
768 
769             res.push('\n');
770             res.push_str("# HELP krill_repo_rrdp_serial RRDP serial\n");
771             res.push_str("# TYPE krill_repo_rrdp_serial counter\n");
772             res.push_str(&format!("krill_repo_rrdp_serial {}\n", stats.serial()));
773 
774             if !server.config.metrics.metrics_hide_publisher_details {
775                 res.push('\n');
776                 res.push_str("# HELP krill_repo_objects number of objects in repository for publisher\n");
777                 res.push_str("# TYPE krill_repo_objects gauge\n");
778                 for (publisher, stats) in publishers {
779                     res.push_str(&format!(
780                         "krill_repo_objects{{publisher=\"{}\"}} {}\n",
781                         publisher,
782                         stats.objects()
783                     ));
784                 }
785 
786                 res.push('\n');
787                 res.push_str("# HELP krill_repo_size size of objects in bytes in repository for publisher\n");
788                 res.push_str("# TYPE krill_repo_size gauge\n");
789                 for (publisher, stats) in publishers {
790                     res.push_str(&format!(
791                         "krill_repo_size{{publisher=\"{}\"}} {}\n",
792                         publisher,
793                         stats.size()
794                     ));
795                 }
796 
797                 res.push('\n');
798                 res.push_str("# HELP krill_repo_last_update unix timestamp in seconds of last update for publisher\n");
799                 res.push_str("# TYPE krill_repo_last_update gauge\n");
800                 for (publisher, stats) in publishers {
801                     if let Some(last_update) = stats.last_update() {
802                         res.push_str(&format!(
803                             "krill_repo_last_update{{publisher=\"{}\"}} {}\n",
804                             publisher,
805                             last_update.timestamp()
806                         ));
807                     }
808                 }
809             }
810         }
811 
812         Ok(HttpResponse::text(res.into_bytes()))
813     } else {
814         Err(req)
815     }
816 }
817 
818 //------------ Publication ---------------------------------------------------
819 
820 /// Handle RFC8181 queries and return the appropriate response.
rfc8181(req: Request) -> RoutingResult821 pub async fn rfc8181(req: Request) -> RoutingResult {
822     if req.path().segment() == "rfc8181" {
823         let mut path = req.path().clone();
824         let publisher = match path.path_arg() {
825             Some(publisher) => publisher,
826             None => return render_error(Error::ApiInvalidHandle),
827         };
828 
829         let state = req.state().clone();
830 
831         let bytes = match req.rfc8181_bytes().await {
832             Ok(bytes) => bytes,
833             Err(e) => return render_error(e),
834         };
835 
836         match state.rfc8181(publisher, bytes) {
837             Ok(bytes) => Ok(HttpResponse::rfc8181(bytes.to_vec())),
838             Err(e) => render_error(e),
839         }
840     } else {
841         Err(req)
842     }
843 }
844 
845 //------------ Embedded TA  --------------------------------------------------
ta(req: Request) -> RoutingResult846 async fn ta(req: Request) -> RoutingResult {
847     match *req.method() {
848         Method::GET => match req.path.full() {
849             "/ta/ta.tal" => tal(req).await,
850             "/testbed.tal" => tal(req).await,
851             "/ta/ta.cer" => ta_cer(req).await,
852             _ => Err(req),
853         },
854         _ => Err(req),
855     }
856 }
857 
tal(req: Request) -> RoutingResult858 pub async fn tal(req: Request) -> RoutingResult {
859     match req.state().ta().await {
860         Ok(ta) => Ok(HttpResponse::text(format!("{}", ta.tal()).into_bytes())),
861         Err(_) => render_unknown_resource(),
862     }
863 }
864 
ta_cer(req: Request) -> RoutingResult865 pub async fn ta_cer(req: Request) -> RoutingResult {
866     match req.state().trust_anchor_cert().await {
867         Some(cert) => Ok(HttpResponse::cert(cert.to_captured().to_vec())),
868         None => render_unknown_resource(),
869     }
870 }
871 
872 //------------ Provisioning (RFC6492) ----------------------------------------
873 
874 /// Process an RFC 6492 request
875 ///
rfc6492(req: Request) -> RoutingResult876 pub async fn rfc6492(req: Request) -> RoutingResult {
877     if req.path().segment() == "rfc6492" {
878         let mut path = req.path().clone();
879         let ca = match path.path_arg() {
880             Some(ca) => ca,
881             None => return render_error(Error::ApiInvalidHandle),
882         };
883 
884         let actor = req.actor();
885         let state = req.state().clone();
886         let user_agent = req.user_agent();
887 
888         let bytes = match req.rfc6492_bytes().await {
889             Ok(bytes) => bytes,
890             Err(e) => return render_error(e),
891         };
892         let krill_server = state;
893         match krill_server.rfc6492(ca, bytes, user_agent, &actor).await {
894             Ok(bytes) => Ok(HttpResponse::rfc6492(bytes.to_vec())),
895             Err(e) => render_error(e),
896         }
897     } else {
898         Err(req)
899     }
900 }
901 
902 /// Return various stats as json
stats(req: Request) -> RoutingResult903 async fn stats(req: Request) -> RoutingResult {
904     match *req.method() {
905         Method::GET => match req.path().full() {
906             "/stats/info" => render_json(req.state().server_info()),
907             "/stats/repo" => render_json_res(req.state().repo_stats()),
908             "/stats/cas" => render_json_res(req.state().cas_stats().await),
909             _ => Err(req),
910         },
911         _ => Err(req),
912     }
913 }
914 
915 // Suppress any error in the unlikely event that we fail to inject the
916 // Authorization header into the HTTP response as this is an internal error that
917 // we should shield the user from, but log a warning as this is very unexpected.
add_authorization_headers_to_response(org_response: HttpResponse, token: Token) -> HttpResponse918 fn add_authorization_headers_to_response(org_response: HttpResponse, token: Token) -> HttpResponse {
919     let mut new_header_names = Vec::new();
920     let mut new_header_values = Vec::new();
921 
922     new_header_names.push(HeaderName::from_str("Authorization"));
923     new_header_values.push(HeaderValue::from_str(&format!("Bearer {}", &token)));
924 
925     let okay = !new_header_names
926         .iter()
927         .zip(new_header_values.iter())
928         .any(|(n, v)| n.is_err() | v.is_err());
929 
930     if okay {
931         let (parts, body) = org_response.response().into_parts();
932         let mut augmented_response = hyper::Response::from_parts(parts, body);
933         let headers = augmented_response.headers_mut();
934         for (name, value) in new_header_names.into_iter().zip(new_header_values.into_iter()) {
935             headers.insert(name.unwrap(), value.unwrap());
936         }
937         HttpResponse::new(augmented_response)
938     } else {
939         let mut conversion_errors = Vec::new();
940         conversion_errors.extend(
941             new_header_names
942                 .into_iter()
943                 .filter(|result| result.is_err())
944                 .map(|i| i.unwrap_err().to_string()),
945         );
946         conversion_errors.extend(
947             new_header_values
948                 .into_iter()
949                 .filter(|result| result.is_err())
950                 .map(|i| i.unwrap_err().to_string()),
951         );
952         warn!(
953             "Internal error: unable to add refreshed auth token to the response: {:?}",
954             conversion_errors.join(", ")
955         );
956         org_response
957     }
958 }
959 
add_new_auth_to_response(res: Result<HttpResponse, Error>, opt_auth: Option<Auth>) -> Result<HttpResponse, Error>960 fn add_new_auth_to_response(res: Result<HttpResponse, Error>, opt_auth: Option<Auth>) -> Result<HttpResponse, Error> {
961     if let Some(Auth::Bearer(token)) = opt_auth {
962         res.map(|ok_res| add_authorization_headers_to_response(ok_res, token))
963     } else {
964         res
965     }
966 }
967 
968 // aa! macro aka if-authorized-then-run-the-given-code-else-return-http-403
969 // ------------------------------------------------------------------------
970 // This macro handles returning from API handler functions if the request is not
971 // Authenticated or lacks sufficient Authorization. We don't use a normal fn for
972 // this as then each API handler function would have to also test for success or
973 // failure and also return the forbidden response to the caller, That would be
974 // both verbose and repetitive. We also can't use the ? operator to return Err
975 // as Err is used to propagate the request to the next handler in the chain. If
976 // we had a child crate we could use a proc macro instead so that we could
977 // "annotate" each API handler function with something like:
978 //   #[require_permission(CA_CREATE)]
979 // Which would insert the generated code at the start of the function body,
980 // similar to how this macro is used in each function.
981 macro_rules! aa {
982     (no_warn $req:ident, $perm:expr, $action:expr) => {{
983         aa!($req, $perm, NO_RESOURCE, $action, true)
984     }};
985     ($req:ident, $perm:expr, $action:expr) => {{
986         aa!($req, $perm, NO_RESOURCE, $action, false)
987     }};
988     (no_warn $req:ident, $perm:expr, $resource:expr, $action:expr) => {{
989         aa!($req, $perm, $resource, $action, true)
990     }};
991     ($req:ident, $perm:expr, $resource:expr, $action:expr) => {{
992         aa!($req, $perm, $resource, $action, false)
993     }};
994     ($req:ident, $perm:expr, $resource:expr, $action:expr, $benign:expr) => {{
995         match $req.actor().is_allowed($perm, $resource) {
996             Ok(true) => $action,
997             Ok(false) => {
998                 let msg = format!(
999                     "User '{}' does not have permission '{}' on resource '{}'",
1000                     $req.actor().name(),
1001                     $perm,
1002                     $resource
1003                 );
1004                 Ok(HttpResponse::forbidden(msg).with_benign($benign))
1005             }
1006             Err(err) => {
1007                 // Avoid an extra round of error -> string -> error conversion
1008                 // which causes the error message to nest, e.g.
1009                 //   "Invalid credentials: Invalid credentials: Session expired"
1010                 match err {
1011                     Error::ApiInvalidCredentials(_)
1012                     | Error::ApiInsufficientRights(_)
1013                     | Error::ApiAuthPermanentError(_)
1014                     | Error::ApiAuthTransientError(_)
1015                     | Error::ApiAuthSessionExpired(_)
1016                     | Error::ApiLoginError(_) => Ok(HttpResponse::response_from_error(err).with_benign($benign)),
1017                     _ => Ok(HttpResponse::forbidden(format!("{}", err)).with_benign($benign)),
1018                 }
1019             }
1020         }
1021     }};
1022 }
1023 
1024 /// Maps the API methods
api(req: Request) -> RoutingResult1025 async fn api(req: Request) -> RoutingResult {
1026     if !req.path().full().starts_with("/api/v1") {
1027         Err(req) // Not for us
1028     } else {
1029         // Eat the first two segments of the path "api/v1"
1030         let mut path = req.path().clone();
1031         path.next(); // gets 'v1' and drops it.
1032 
1033         match path.next() {
1034             Some("authorized") => api_authorized(req).await,
1035             restricted_endpoint => {
1036                 // Make sure access is allowed
1037                 aa!(req, Permission::LOGIN, {
1038                     match restricted_endpoint {
1039                         Some("bulk") => api_bulk(req, &mut path).await,
1040                         Some("cas") => api_cas(req, &mut path).await,
1041                         Some("pubd") => aa!(req, Permission::PUB_ADMIN, api_publication_server(req, &mut path).await),
1042                         _ => render_unknown_method(),
1043                     }
1044                 })
1045             }
1046         }
1047     }
1048 }
1049 
api_authorized(req: Request) -> RoutingResult1050 async fn api_authorized(req: Request) -> RoutingResult {
1051     // Use 'no_warn' to prevent the log being filled with warnings about
1052     // insufficient user rights as this API endpoint is invoked by Lagosta on
1053     // every view transition, and not being authorized is a valid state that
1054     // triggers Lagosta to show a login form, not something to warn about!
1055     aa!(no_warn
1056         req,
1057         Permission::LOGIN,
1058         match *req.method() {
1059             Method::GET => render_ok(),
1060             _ => render_unknown_method(),
1061         }
1062     )
1063 }
1064 
api_bulk(req: Request, path: &mut RequestPath) -> RoutingResult1065 async fn api_bulk(req: Request, path: &mut RequestPath) -> RoutingResult {
1066     match path.full() {
1067         "/api/v1/bulk/cas/issues" => api_all_ca_issues(req).await,
1068         "/api/v1/bulk/cas/sync/parent" => api_refresh_all(req).await,
1069         "/api/v1/bulk/cas/sync/repo" => api_resync_all(req).await,
1070         "/api/v1/bulk/cas/publish" => api_republish_all(req).await,
1071         _ => render_unknown_method(),
1072     }
1073 }
1074 
api_cas(req: Request, path: &mut RequestPath) -> RoutingResult1075 async fn api_cas(req: Request, path: &mut RequestPath) -> RoutingResult {
1076     match path.path_arg::<Handle>() {
1077         Some(ca) => aa!(req, Permission::CA_READ, ca.clone(), {
1078             match path.next() {
1079                 None => match *req.method() {
1080                     Method::GET => api_ca_info(req, ca).await,
1081                     Method::DELETE => api_ca_delete(req, ca).await,
1082                     _ => render_unknown_method(),
1083                 },
1084                 Some("aspas") => api_ca_aspas(req, path, ca).await,
1085                 Some("children") => api_ca_children(req, path, ca).await,
1086                 Some("history") => api_ca_history(req, path, ca).await,
1087 
1088                 Some("id") => api_ca_id(req, path, ca).await,
1089                 Some("issues") => api_ca_issues(req, ca).await,
1090                 Some("keys") => api_ca_keys(req, path, ca).await,
1091                 Some("parents") => api_ca_parents(req, path, ca).await,
1092                 Some("repo") => api_ca_repo(req, path, ca).await,
1093                 Some("routes") => api_ca_routes(req, path, ca).await,
1094                 Some("stats") => api_ca_stats(req, path, ca).await,
1095                 Some("sync") => api_ca_sync(req, path, ca).await,
1096 
1097                 Some("rta") => api_ca_rta(req, path, ca).await,
1098 
1099                 _ => render_unknown_method(),
1100             }
1101         }),
1102         None => match *req.method() {
1103             Method::GET => api_cas_list(req).await,
1104             Method::POST => api_ca_init(req).await,
1105             _ => render_unknown_method(),
1106         },
1107     }
1108 }
1109 
api_ca_keys(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1110 async fn api_ca_keys(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1111     match *req.method() {
1112         Method::POST => match path.next() {
1113             Some("roll_init") => api_ca_kr_init(req, ca).await,
1114             Some("roll_activate") => api_ca_kr_activate(req, ca).await,
1115             _ => render_unknown_method(),
1116         },
1117         _ => render_unknown_method(),
1118     }
1119 }
1120 
api_ca_parents(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1121 async fn api_ca_parents(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1122     if let Some(parent) = path.path_arg() {
1123         match *req.method() {
1124             Method::GET => api_ca_my_parent_contact(req, ca, parent).await,
1125             Method::POST => api_ca_parent_add_or_update(req, ca, Some(parent)).await,
1126             Method::DELETE => api_ca_remove_parent(req, ca, parent).await,
1127             _ => render_unknown_method(),
1128         }
1129     } else {
1130         match *req.method() {
1131             Method::GET => api_ca_my_parent_statuses(req, ca).await,
1132             Method::POST => api_ca_parent_add_or_update(req, ca, None).await,
1133             _ => render_unknown_method(),
1134         }
1135     }
1136 }
1137 
api_ca_repo(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1138 async fn api_ca_repo(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1139     match path.next() {
1140         None => match *req.method() {
1141             Method::GET => api_ca_repo_details(req, ca).await,
1142             Method::POST => api_ca_repo_update(req, ca).await,
1143             _ => render_unknown_method(),
1144         },
1145         Some("status") => api_ca_repo_status(req, ca).await,
1146         _ => render_unknown_method(),
1147     }
1148 }
1149 
api_ca_routes(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1150 async fn api_ca_routes(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1151     match path.next() {
1152         None => match *req.method() {
1153             Method::GET => api_ca_routes_show(req, ca).await,
1154             Method::POST => api_ca_routes_update(req, ca).await,
1155             _ => render_unknown_method(),
1156         },
1157         Some("try") => match *req.method() {
1158             Method::POST => api_ca_routes_try_update(req, ca).await,
1159             _ => render_unknown_method(),
1160         },
1161         Some("analysis") => api_ca_routes_analysis(req, path, ca).await,
1162         _ => render_unknown_method(),
1163     }
1164 }
1165 
api_ca_stats(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1166 async fn api_ca_stats(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1167     match path.next() {
1168         Some("children") => match path.next() {
1169             Some("connections") => api_ca_stats_child_connections(req, ca).await,
1170             _ => render_unknown_method(),
1171         },
1172         _ => render_unknown_method(),
1173     }
1174 }
1175 
api_ca_sync(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1176 async fn api_ca_sync(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1177     aa!(req, Permission::CA_UPDATE, ca.clone(), {
1178         if req.is_post() {
1179             let actor = req.actor();
1180             match path.next() {
1181                 Some("parents") => render_empty_res(req.state().cas_refresh_single(ca, &actor).await),
1182                 Some("repo") => render_empty_res(req.state().cas_repo_sync_single(&ca).await),
1183                 _ => render_unknown_method(),
1184             }
1185         } else {
1186             render_unknown_method()
1187         }
1188     })
1189 }
1190 
api_publication_server(req: Request, path: &mut RequestPath) -> RoutingResult1191 async fn api_publication_server(req: Request, path: &mut RequestPath) -> RoutingResult {
1192     match path.next() {
1193         Some("publishers") => api_publishers(req, path).await,
1194         Some("stale") => api_stale_publishers(req, path.next()).await,
1195         Some("init") => match *req.method() {
1196             Method::POST => {
1197                 let state = req.state.clone();
1198                 match req.json().await {
1199                     Ok(uris) => render_empty_res(state.repository_init(uris)),
1200                     Err(e) => render_error(e),
1201                 }
1202             }
1203             Method::DELETE => render_empty_res(req.state.repository_clear()),
1204             _ => render_unknown_method(),
1205         },
1206         _ => render_unknown_method(),
1207     }
1208 }
1209 
api_publishers(req: Request, path: &mut RequestPath) -> RoutingResult1210 async fn api_publishers(req: Request, path: &mut RequestPath) -> RoutingResult {
1211     match *req.method() {
1212         Method::GET => match path.path_arg() {
1213             Some(publisher) => match path.next() {
1214                 None => api_show_pbl(req, publisher).await,
1215                 Some("response.xml") => api_repository_response_xml(req, publisher).await,
1216                 Some("response.json") => api_repository_response_json(req, publisher).await,
1217 
1218                 _ => render_unknown_method(),
1219             },
1220             None => api_list_pbl(req).await,
1221         },
1222         Method::POST => match path.next() {
1223             None => api_add_pbl(req).await,
1224             _ => render_unknown_method(),
1225         },
1226         Method::DELETE => match path.path_arg() {
1227             Some(publisher) => api_remove_pbl(req, publisher).await,
1228             None => render_error(Error::ApiInvalidHandle),
1229         },
1230         _ => render_unknown_method(),
1231     }
1232 }
1233 
1234 //------------ Admin: Publishers ---------------------------------------------
1235 
1236 /// Returns a list of publisher which have not updated for more
1237 /// than the given number of seconds.
api_stale_publishers(req: Request, seconds: Option<&str>) -> RoutingResult1238 pub async fn api_stale_publishers(req: Request, seconds: Option<&str>) -> RoutingResult {
1239     aa!(req, Permission::PUB_LIST, {
1240         let seconds = seconds.unwrap_or("");
1241         match i64::from_str(seconds) {
1242             Ok(seconds) => render_json_res(
1243                 req.state()
1244                     .repo_stats()
1245                     .map(|stats| PublisherList::build(&stats.stale_publishers(seconds))),
1246             ),
1247             Err(_) => render_error(Error::ApiInvalidSeconds),
1248         }
1249     })
1250 }
1251 
1252 /// Returns a json structure with all publishers in it.
api_list_pbl(req: Request) -> RoutingResult1253 pub async fn api_list_pbl(req: Request) -> RoutingResult {
1254     aa!(req, Permission::PUB_LIST, {
1255         render_json_res(
1256             req.state()
1257                 .publishers()
1258                 .map(|publishers| PublisherList::build(&publishers)),
1259         )
1260     })
1261 }
1262 
1263 /// Adds a publisher
api_add_pbl(req: Request) -> RoutingResult1264 pub async fn api_add_pbl(req: Request) -> RoutingResult {
1265     aa!(req, Permission::PUB_CREATE, {
1266         let actor = req.actor();
1267         let server = req.state().clone();
1268         match req.json().await {
1269             Ok(pbl) => render_json_res(server.add_publisher(pbl, &actor)),
1270             Err(e) => render_error(e),
1271         }
1272     })
1273 }
1274 
1275 /// Removes a publisher. Should be idempotent! If if did not exist then
1276 /// that's just fine.
1277 #[allow(clippy::redundant_clone)] // false positive
api_remove_pbl(req: Request, publisher: Handle) -> RoutingResult1278 pub async fn api_remove_pbl(req: Request, publisher: Handle) -> RoutingResult {
1279     aa!(req, Permission::PUB_DELETE, publisher.clone(), {
1280         let actor = req.actor();
1281         render_empty_res(req.state().remove_publisher(publisher, &actor))
1282     })
1283 }
1284 
1285 /// Returns a json structure with publisher details
1286 #[allow(clippy::redundant_clone)] // false positive
api_show_pbl(req: Request, publisher: Handle) -> RoutingResult1287 pub async fn api_show_pbl(req: Request, publisher: Handle) -> RoutingResult {
1288     aa!(
1289         req,
1290         Permission::PUB_READ,
1291         publisher.clone(),
1292         render_json_res(req.state().get_publisher(&publisher))
1293     )
1294 }
1295 
1296 //------------ repository_response ---------------------------------------------
1297 
1298 #[allow(clippy::redundant_clone)] // false positive
api_repository_response_xml(req: Request, publisher: Handle) -> RoutingResult1299 pub async fn api_repository_response_xml(req: Request, publisher: Handle) -> RoutingResult {
1300     aa!(req, Permission::PUB_READ, publisher.clone(), {
1301         match repository_response(&req, &publisher).await {
1302             Ok(res) => Ok(HttpResponse::xml(res.encode_vec())),
1303             Err(e) => render_error(e),
1304         }
1305     })
1306 }
1307 
1308 #[allow(clippy::redundant_clone)] // false positive
api_repository_response_json(req: Request, publisher: Handle) -> RoutingResult1309 pub async fn api_repository_response_json(req: Request, publisher: Handle) -> RoutingResult {
1310     aa!(req, Permission::PUB_READ, publisher.clone(), {
1311         match repository_response(&req, &publisher).await {
1312             Ok(res) => render_json(res),
1313             Err(e) => render_error(e),
1314         }
1315     })
1316 }
1317 
repository_response(req: &Request, publisher: &Handle) -> Result<rfc8183::RepositoryResponse, Error>1318 async fn repository_response(req: &Request, publisher: &Handle) -> Result<rfc8183::RepositoryResponse, Error> {
1319     req.state().repository_response(publisher)
1320 }
1321 
api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResult1322 pub async fn api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResult {
1323     aa!(req, Permission::CA_UPDATE, parent.clone(), {
1324         let actor = req.actor();
1325         let server = req.state().clone();
1326         match req.json().await {
1327             Ok(child_req) => render_json_res(server.ca_add_child(&parent, child_req, &actor).await),
1328             Err(e) => render_error(e),
1329         }
1330     })
1331 }
1332 
api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1333 async fn api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1334     aa!(req, Permission::CA_UPDATE, child.clone(), {
1335         let actor = req.actor();
1336         let server = req.state().clone();
1337         match req.json().await {
1338             Ok(child_req) => render_empty_res(server.ca_child_update(&ca, child, child_req, &actor).await),
1339             Err(e) => render_error(e),
1340         }
1341     })
1342 }
1343 
api_ca_child_remove(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1344 pub async fn api_ca_child_remove(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1345     aa!(req, Permission::CA_UPDATE, ca.clone(), {
1346         let actor = req.actor();
1347         render_empty_res(req.state().ca_child_remove(&ca, child, &actor).await)
1348     })
1349 }
1350 
api_ca_child_show(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1351 async fn api_ca_child_show(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1352     aa!(
1353         req,
1354         Permission::CA_READ,
1355         ca.clone(),
1356         render_json_res(req.state().ca_child_show(&ca, &child).await)
1357     )
1358 }
1359 
api_ca_stats_child_connections(req: Request, ca: Handle) -> RoutingResult1360 async fn api_ca_stats_child_connections(req: Request, ca: Handle) -> RoutingResult {
1361     aa!(
1362         req,
1363         Permission::CA_READ,
1364         ca.clone(),
1365         render_json_res(req.state().ca_stats_child_connections(&ca).await)
1366     )
1367 }
1368 
api_ca_parent_contact(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1369 async fn api_ca_parent_contact(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1370     aa!(
1371         req,
1372         Permission::CA_READ,
1373         ca.clone(),
1374         render_json_res(req.state().ca_parent_contact(&ca, child.clone()).await)
1375     )
1376 }
1377 
api_ca_parent_res_json(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1378 async fn api_ca_parent_res_json(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1379     aa!(
1380         req,
1381         Permission::CA_READ,
1382         ca.clone(),
1383         render_json_res(req.state().ca_parent_response(&ca, child.clone()).await)
1384     )
1385 }
1386 
api_ca_parent_res_xml(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1387 pub async fn api_ca_parent_res_xml(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1388     aa!(req, Permission::CA_READ, ca.clone(), {
1389         match req.state().ca_parent_response(&ca, child.clone()).await {
1390             Ok(res) => Ok(HttpResponse::xml(res.encode_vec())),
1391             Err(e) => render_error(e),
1392         }
1393     })
1394 }
1395 
1396 //------------ Admin: CertAuth -----------------------------------------------
1397 
api_all_ca_issues(req: Request) -> RoutingResult1398 async fn api_all_ca_issues(req: Request) -> RoutingResult {
1399     match *req.method() {
1400         Method::GET => aa!(req, Permission::CA_READ, {
1401             let actor = req.actor();
1402             render_json_res(req.state().all_ca_issues(&actor).await)
1403         }),
1404         _ => render_unknown_method(),
1405     }
1406 }
1407 
1408 /// Returns the health (state) for a given CA.
api_ca_issues(req: Request, ca: Handle) -> RoutingResult1409 async fn api_ca_issues(req: Request, ca: Handle) -> RoutingResult {
1410     match *req.method() {
1411         Method::GET => aa!(
1412             req,
1413             Permission::CA_READ,
1414             ca.clone(),
1415             render_json_res(req.state().ca_issues(&ca).await)
1416         ),
1417         _ => render_unknown_method(),
1418     }
1419 }
1420 
api_cas_list(req: Request) -> RoutingResult1421 async fn api_cas_list(req: Request) -> RoutingResult {
1422     aa!(req, Permission::CA_LIST, {
1423         let actor = req.actor();
1424         render_json_res(req.state().ca_list(&actor))
1425     })
1426 }
1427 
api_ca_init(req: Request) -> RoutingResult1428 pub async fn api_ca_init(req: Request) -> RoutingResult {
1429     aa!(req, Permission::CA_CREATE, {
1430         let state = req.state().clone();
1431 
1432         match req.json().await {
1433             Ok(ca_init) => render_empty_res(state.ca_init(ca_init)),
1434             Err(e) => render_error(e),
1435         }
1436     })
1437 }
1438 
api_ca_id(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1439 async fn api_ca_id(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1440     match *req.method() {
1441         Method::POST => aa!(req, Permission::CA_UPDATE, ca.clone(), {
1442             let actor = req.actor();
1443             render_empty_res(req.state().ca_update_id(ca, &actor).await)
1444         }),
1445         Method::GET => match path.next() {
1446             Some("child_request.xml") => api_ca_child_req_xml(req, ca).await,
1447             Some("child_request.json") => api_ca_child_req_json(req, ca).await,
1448             Some("publisher_request.json") => api_ca_publisher_req_json(req, ca).await,
1449             Some("publisher_request.xml") => api_ca_publisher_req_xml(req, ca).await,
1450             _ => render_unknown_method(),
1451         },
1452         _ => render_unknown_method(),
1453     }
1454 }
1455 
api_ca_info(req: Request, handle: Handle) -> RoutingResult1456 async fn api_ca_info(req: Request, handle: Handle) -> RoutingResult {
1457     aa!(
1458         req,
1459         Permission::CA_READ,
1460         handle.clone(),
1461         render_json_res(req.state().ca_info(&handle).await)
1462     )
1463 }
1464 
api_ca_delete(req: Request, handle: Handle) -> RoutingResult1465 async fn api_ca_delete(req: Request, handle: Handle) -> RoutingResult {
1466     let actor = req.actor();
1467     aa!(
1468         req,
1469         Permission::CA_DELETE,
1470         handle.clone(),
1471         render_json_res(req.state().ca_delete(&handle, &actor).await)
1472     )
1473 }
1474 
api_ca_my_parent_contact(req: Request, ca: Handle, parent: ParentHandle) -> RoutingResult1475 async fn api_ca_my_parent_contact(req: Request, ca: Handle, parent: ParentHandle) -> RoutingResult {
1476     aa!(
1477         req,
1478         Permission::CA_READ,
1479         ca.clone(),
1480         render_json_res(req.state().ca_my_parent_contact(&ca, &parent).await)
1481     )
1482 }
1483 
api_ca_my_parent_statuses(req: Request, ca: Handle) -> RoutingResult1484 async fn api_ca_my_parent_statuses(req: Request, ca: Handle) -> RoutingResult {
1485     aa!(
1486         req,
1487         Permission::CA_READ,
1488         ca.clone(),
1489         render_json_res(req.state().ca_status(&ca).await.map(|s| s.parents().clone()))
1490     )
1491 }
1492 
api_ca_aspas(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1493 async fn api_ca_aspas(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1494     match path.next() {
1495         None => match *req.method() {
1496             Method::GET => api_ca_aspas_definitions_show(req, ca).await,
1497             Method::POST => api_ca_aspas_definitions_update(req, ca).await,
1498             _ => render_unknown_method(),
1499         },
1500         // We may need other functions in future, such as 'analyze' or 'try'.
1501         // So keep the base namespace clean and use '/api/v1/aspas/as/<asn>/..'
1502         // for functions on specific ASPA definitions for the given (customer)
1503         // ASN.
1504         Some("as") => {
1505             // get as path parameter, or error
1506             // - get (specific definition)
1507             // - delete
1508             // - update? (definition includes the ASN so this can be in the base path)
1509             match path.path_arg() {
1510                 Some(customer) => match *req.method() {
1511                     Method::POST => api_ca_aspas_update_aspa(req, ca, customer).await,
1512                     Method::DELETE => api_ca_aspas_delete(req, ca, customer).await,
1513                     _ => render_unknown_method(),
1514                 },
1515                 None => render_unknown_method(),
1516             }
1517         }
1518         _ => render_unknown_method(),
1519     }
1520 }
1521 
api_ca_children(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1522 async fn api_ca_children(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1523     match path.path_arg() {
1524         Some(child) => match path.next() {
1525             None => match *req.method() {
1526                 Method::GET => api_ca_child_show(req, ca, child).await,
1527                 Method::POST => api_ca_child_update(req, ca, child).await,
1528                 Method::DELETE => api_ca_child_remove(req, ca, child).await,
1529                 _ => render_unknown_method(),
1530             },
1531             Some("contact") => api_ca_parent_contact(req, ca, child).await,
1532             Some("parent_response.json") => api_ca_parent_res_json(req, ca, child).await,
1533             Some("parent_response.xml") => api_ca_parent_res_xml(req, ca, child).await,
1534             _ => render_unknown_method(),
1535         },
1536         None => match *req.method() {
1537             Method::POST => api_ca_add_child(req, ca).await,
1538             _ => render_unknown_method(),
1539         },
1540     }
1541 }
1542 
api_ca_history_commands(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult1543 async fn api_ca_history_commands(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult {
1544     match *req.method() {
1545         Method::GET => aa!(req, Permission::CA_READ, handle.clone(), {
1546             // /api/v1/cas/{ca}/history/commands  /<rows>/<offset>/<after>/<before>
1547             let mut crit = CommandHistoryCriteria::default();
1548 
1549             if let Some(rows) = path.path_arg() {
1550                 crit.set_rows(rows);
1551             }
1552 
1553             if let Some(offset) = path.path_arg() {
1554                 crit.set_offset(offset);
1555             }
1556 
1557             if let Some(after) = path.path_arg() {
1558                 crit.set_after(after);
1559             }
1560 
1561             if let Some(before) = path.path_arg() {
1562                 crit.set_before(before);
1563             }
1564             match req.state().ca_history(&handle, crit).await {
1565                 Ok(history) => render_json(history),
1566                 Err(e) => render_error(e),
1567             }
1568         }),
1569         _ => render_unknown_method(),
1570     }
1571 }
1572 
api_ca_history(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1573 async fn api_ca_history(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1574     match path.next() {
1575         Some("details") => api_ca_command_details(req, path, ca).await,
1576         Some("commands") => api_ca_history_commands(req, path, ca).await,
1577         _ => render_unknown_method(),
1578     }
1579 }
1580 
1581 #[allow(clippy::redundant_clone)] // false positive
api_ca_command_details(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult1582 async fn api_ca_command_details(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult {
1583     // /api/v1/cas/{ca}/command/<command-key>
1584     match path.path_arg() {
1585         Some(key) => match *req.method() {
1586             Method::GET => aa!(req, Permission::CA_READ, handle.clone(), {
1587                 match req.state().ca_command_details(&handle, key) {
1588                     Ok(details) => render_json(details),
1589                     Err(e) => match e {
1590                         Error::AggregateStoreError(AggregateStoreError::UnknownCommand(_, _)) => {
1591                             render_unknown_resource()
1592                         }
1593                         _ => render_error(e),
1594                     },
1595                 }
1596             }),
1597             _ => render_unknown_method(),
1598         },
1599         None => render_unknown_resource(),
1600     }
1601 }
1602 
api_ca_child_req_xml(req: Request, handle: Handle) -> RoutingResult1603 async fn api_ca_child_req_xml(req: Request, handle: Handle) -> RoutingResult {
1604     match *req.method() {
1605         Method::GET => aa!(
1606             req,
1607             Permission::CA_READ,
1608             handle.clone(),
1609             match ca_child_req(&req, &handle).await {
1610                 Ok(req) => Ok(HttpResponse::xml(req.encode_vec())),
1611                 Err(e) => render_error(e),
1612             }
1613         ),
1614         _ => render_unknown_method(),
1615     }
1616 }
1617 
api_ca_child_req_json(req: Request, handle: Handle) -> RoutingResult1618 async fn api_ca_child_req_json(req: Request, handle: Handle) -> RoutingResult {
1619     match *req.method() {
1620         Method::GET => aa!(
1621             req,
1622             Permission::CA_READ,
1623             handle.clone(),
1624             match ca_child_req(&req, &handle).await {
1625                 Ok(req) => render_json(req),
1626                 Err(e) => render_error(e),
1627             }
1628         ),
1629         _ => render_unknown_method(),
1630     }
1631 }
1632 
ca_child_req(req: &Request, handle: &Handle) -> Result<rfc8183::ChildRequest, Error>1633 async fn ca_child_req(req: &Request, handle: &Handle) -> Result<rfc8183::ChildRequest, Error> {
1634     req.state().ca_child_req(handle).await
1635 }
1636 
api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResult1637 async fn api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResult {
1638     match *req.method() {
1639         Method::GET => aa!(
1640             req,
1641             Permission::CA_READ,
1642             handle.clone(),
1643             render_json_res(req.state().ca_publisher_req(&handle).await)
1644         ),
1645         _ => render_unknown_method(),
1646     }
1647 }
1648 
api_ca_publisher_req_xml(req: Request, handle: Handle) -> RoutingResult1649 async fn api_ca_publisher_req_xml(req: Request, handle: Handle) -> RoutingResult {
1650     match *req.method() {
1651         Method::GET => aa!(
1652             req,
1653             Permission::CA_READ,
1654             handle.clone(),
1655             match req.state().ca_publisher_req(&handle).await {
1656                 Ok(res) => Ok(HttpResponse::xml(res.encode_vec())),
1657                 Err(e) => render_error(e),
1658             }
1659         ),
1660         _ => render_unknown_method(),
1661     }
1662 }
1663 
api_ca_repo_details(req: Request, handle: Handle) -> RoutingResult1664 async fn api_ca_repo_details(req: Request, handle: Handle) -> RoutingResult {
1665     aa!(
1666         req,
1667         Permission::CA_READ,
1668         handle.clone(),
1669         render_json_res(req.state().ca_repo_details(&handle).await)
1670     )
1671 }
1672 
api_ca_repo_status(req: Request, handle: Handle) -> RoutingResult1673 async fn api_ca_repo_status(req: Request, handle: Handle) -> RoutingResult {
1674     match *req.method() {
1675         Method::GET => aa!(
1676             req,
1677             Permission::CA_READ,
1678             handle.clone(),
1679             render_json_res(req.state().ca_status(&handle).await.map(|status| status.repo().clone()))
1680         ),
1681         _ => render_unknown_method(),
1682     }
1683 }
1684 
extract_repository_contact(handle: &Handle, bytes: Bytes) -> Result<RepositoryContact, Error>1685 fn extract_repository_contact(handle: &Handle, bytes: Bytes) -> Result<RepositoryContact, Error> {
1686     let string = String::from_utf8(bytes.to_vec()).map_err(Error::custom)?;
1687 
1688     // TODO: Switch based on Content-Type header
1689     if string.starts_with('<') {
1690         if string.contains("<parent_response") {
1691             Err(Error::CaRepoResponseWrongXml(handle.clone()))
1692         } else {
1693             let response = rfc8183::RepositoryResponse::validate(string.as_bytes())
1694                 .map_err(|e| Error::CaRepoResponseInvalidXml(handle.clone(), e.to_string()))?;
1695             Ok(RepositoryContact::new(response))
1696         }
1697     } else {
1698         serde_json::from_str(&string).map_err(Error::JsonError)
1699     }
1700 }
1701 
api_ca_repo_update(req: Request, handle: Handle) -> RoutingResult1702 async fn api_ca_repo_update(req: Request, handle: Handle) -> RoutingResult {
1703     aa!(req, Permission::CA_UPDATE, handle.clone(), {
1704         let actor = req.actor();
1705         let server = req.state().clone();
1706 
1707         match req
1708             .api_bytes()
1709             .await
1710             .map(|bytes| extract_repository_contact(&handle, bytes))
1711         {
1712             Ok(Ok(update)) => render_empty_res(server.ca_repo_update(handle, update, &actor).await),
1713             Ok(Err(e)) | Err(e) => render_error(e),
1714         }
1715     })
1716 }
1717 
api_ca_parent_add_or_update(req: Request, ca: Handle, parent_override: Option<Handle>) -> RoutingResult1718 async fn api_ca_parent_add_or_update(req: Request, ca: Handle, parent_override: Option<Handle>) -> RoutingResult {
1719     aa!(req, Permission::CA_UPDATE, ca.clone(), {
1720         let actor = req.actor();
1721         let server = req.state().clone();
1722 
1723         let bytes = match req.api_bytes().await {
1724             Ok(bytes) => bytes,
1725             Err(e) => return render_error(e),
1726         };
1727 
1728         match extract_parent_ca_req(&ca, bytes, parent_override) {
1729             Ok(parent_req) => render_empty_res(server.ca_parent_add_or_update(ca, parent_req, &actor).await),
1730             Err(e) => render_error(e),
1731         }
1732     })
1733 }
1734 
extract_parent_ca_req(ca: &Handle, bytes: Bytes, parent_override: Option<Handle>) -> Result<ParentCaReq, Error>1735 fn extract_parent_ca_req(ca: &Handle, bytes: Bytes, parent_override: Option<Handle>) -> Result<ParentCaReq, Error> {
1736     let string = String::from_utf8(bytes.to_vec()).map_err(Error::custom)?;
1737 
1738     // TODO: Switch based on Content-Type header
1739     let req = if string.starts_with('<') {
1740         if string.starts_with("<repository") {
1741             return Err(Error::CaParentResponseWrongXml(ca.clone()));
1742         } else {
1743             let res = rfc8183::ParentResponse::validate(string.as_bytes())
1744                 .map_err(|e| Error::CaParentResponseInvalidXml(ca.clone(), e.to_string()))?;
1745 
1746             let parent_name = parent_override.unwrap_or_else(|| res.parent_handle().clone());
1747             let contact = ParentCaContact::for_rfc6492(res);
1748             ParentCaReq::new(parent_name, contact)
1749         }
1750     } else {
1751         let req: ParentCaReq = serde_json::from_str(&string).map_err(Error::JsonError)?;
1752         if let Some(parent_override) = parent_override {
1753             if req.handle() != &parent_override {
1754                 return Err(Error::Custom(format!(
1755                     "Used different parent names on path ({}) and submitted JSON ({}) for adding/updating a parent",
1756                     parent_override,
1757                     req.handle()
1758                 )));
1759             }
1760         }
1761         req
1762     };
1763 
1764     Ok(req)
1765 }
1766 
api_ca_remove_parent(req: Request, ca: Handle, parent: Handle) -> RoutingResult1767 async fn api_ca_remove_parent(req: Request, ca: Handle, parent: Handle) -> RoutingResult {
1768     aa!(req, Permission::CA_UPDATE, ca.clone(), {
1769         let actor = req.actor();
1770         render_empty_res(req.state().ca_parent_remove(ca, parent, &actor).await)
1771     })
1772 }
1773 
1774 /// Force a key roll for a CA, i.e. use a max key age of 0 seconds.
api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult1775 async fn api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult {
1776     aa!(req, Permission::CA_UPDATE, ca.clone(), {
1777         let actor = req.actor();
1778         render_empty_res(req.state().ca_keyroll_init(ca, &actor).await)
1779     })
1780 }
1781 
1782 /// Force key activation for all new keys, i.e. use a staging period of 0 seconds.
api_ca_kr_activate(req: Request, ca: Handle) -> RoutingResult1783 async fn api_ca_kr_activate(req: Request, ca: Handle) -> RoutingResult {
1784     aa!(req, Permission::CA_UPDATE, ca.clone(), {
1785         let actor = req.actor();
1786         render_empty_res(req.state().ca_keyroll_activate(ca, &actor).await)
1787     })
1788 }
1789 
1790 // -- ASPA functions
1791 
1792 /// List the current ASPA definitions for a CA
api_ca_aspas_definitions_show(req: Request, ca: Handle) -> RoutingResult1793 async fn api_ca_aspas_definitions_show(req: Request, ca: Handle) -> RoutingResult {
1794     aa!(req, Permission::ASPAS_READ, ca.clone(), {
1795         let state = req.state().clone();
1796         render_json_res(state.ca_aspas_definitions_show(ca).await)
1797     })
1798 }
1799 
1800 /// Add a new ASPA definition for a CA based on the update in the POST
api_ca_aspas_definitions_update(req: Request, ca: Handle) -> RoutingResult1801 async fn api_ca_aspas_definitions_update(req: Request, ca: Handle) -> RoutingResult {
1802     aa!(req, Permission::ASPAS_UPDATE, ca.clone(), {
1803         let actor = req.actor();
1804         let state = req.state().clone();
1805 
1806         match req.json().await {
1807             Err(e) => render_error(e),
1808             Ok(updates) => render_empty_res(state.ca_aspas_definitions_update(ca, updates, &actor).await),
1809         }
1810     })
1811 }
1812 
1813 /// Update an existing ASPA definition for a CA based on the update in the POST
api_ca_aspas_update_aspa(req: Request, ca: Handle, customer: AsId) -> RoutingResult1814 async fn api_ca_aspas_update_aspa(req: Request, ca: Handle, customer: AsId) -> RoutingResult {
1815     aa!(req, Permission::ASPAS_UPDATE, ca.clone(), {
1816         let actor = req.actor();
1817         let state = req.state().clone();
1818 
1819         match req.json().await {
1820             Err(e) => render_error(e),
1821             Ok(update) => render_empty_res(state.ca_aspas_update_aspa(ca, customer, update, &actor).await),
1822         }
1823     })
1824 }
1825 
1826 /// Delete the ASPA definition for the given CA and customer ASN
api_ca_aspas_delete(req: Request, ca: Handle, customer: AsId) -> RoutingResult1827 async fn api_ca_aspas_delete(req: Request, ca: Handle, customer: AsId) -> RoutingResult {
1828     aa!(req, Permission::ASPAS_UPDATE, ca.clone(), {
1829         let actor = req.actor();
1830         let state = req.state().clone();
1831 
1832         let updates = AspaDefinitionUpdates::new(vec![], vec![customer]);
1833         render_empty_res(state.ca_aspas_definitions_update(ca, updates, &actor).await)
1834     })
1835 }
1836 
1837 /// Update the route authorizations for this CA
api_ca_routes_update(req: Request, ca: Handle) -> RoutingResult1838 async fn api_ca_routes_update(req: Request, ca: Handle) -> RoutingResult {
1839     aa!(req, Permission::ROUTES_UPDATE, ca.clone(), {
1840         let actor = req.actor();
1841         let state = req.state().clone();
1842 
1843         match req.json().await {
1844             Err(e) => render_error(e),
1845             Ok(updates) => render_empty_res(state.ca_routes_update(ca, updates, &actor).await),
1846         }
1847     })
1848 }
1849 
1850 /// Tries an update. If the dry-run for it would be successful, and the analysis
1851 /// for the resources in the update have no remaining invalids, apply it. Otherwise
1852 /// return the analysis and a suggestion.
api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult1853 async fn api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult {
1854     aa!(req, Permission::ROUTES_UPDATE, ca.clone(), {
1855         let actor = req.actor();
1856         let state = req.state().clone();
1857 
1858         match req.json::<RoaDefinitionUpdates>().await {
1859             Err(e) => render_error(e),
1860             Ok(updates) => {
1861                 let server = state;
1862                 match server.ca_routes_bgp_dry_run(&ca, updates.clone()).await {
1863                     Err(e) => {
1864                         // update was rejected, return error
1865                         render_error(e)
1866                     }
1867                     Ok(effect) => {
1868                         if !effect.contains_invalids() {
1869                             // no issues found, apply
1870                             render_empty_res(server.ca_routes_update(ca, updates, &actor).await)
1871                         } else {
1872                             // remaining invalids exist, advise user
1873                             let updates: RouteAuthorizationUpdates = updates.into();
1874                             let updates = updates.into_explicit();
1875                             let resources = updates.affected_prefixes();
1876 
1877                             match server.ca_routes_bgp_suggest(&ca, Some(resources)).await {
1878                                 Err(e) => render_error(e), // should not fail after dry run, but hey..
1879                                 Ok(suggestion) => render_json(BgpAnalysisAdvice::new(effect, suggestion)),
1880                             }
1881                         }
1882                     }
1883                 }
1884             }
1885         }
1886     })
1887 }
1888 
1889 /// show the route authorizations for this CA
api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult1890 async fn api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult {
1891     aa!(req, Permission::ROUTES_READ, ca.clone(), {
1892         match req.state().ca_routes_show(&ca).await {
1893             Ok(roas) => render_json(roas),
1894             Err(_) => render_unknown_resource(),
1895         }
1896     })
1897 }
1898 
1899 /// Show the state of ROAs vs BGP for this CA
api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1900 async fn api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1901     aa!(req, Permission::ROUTES_ANALYSIS, ca.clone(), {
1902         match path.next() {
1903             Some("full") => render_json_res(req.state().ca_routes_bgp_analysis(&ca).await),
1904             Some("dryrun") => match *req.method() {
1905                 Method::POST => {
1906                     let state = req.state.clone();
1907                     match req.json().await {
1908                         Err(e) => render_error(e),
1909                         Ok(updates) => render_json_res(state.ca_routes_bgp_dry_run(&ca, updates).await),
1910                     }
1911                 }
1912                 _ => render_unknown_method(),
1913             },
1914             Some("suggest") => match *req.method() {
1915                 Method::GET => render_json_res(req.state().ca_routes_bgp_suggest(&ca, None).await),
1916                 Method::POST => {
1917                     let server = req.state().clone();
1918                     match req.json().await {
1919                         Err(e) => render_error(e),
1920                         Ok(resources) => render_json_res(server.ca_routes_bgp_suggest(&ca, Some(resources)).await),
1921                     }
1922                 }
1923                 _ => render_unknown_method(),
1924             },
1925             _ => render_unknown_method(),
1926         }
1927     })
1928 }
1929 
1930 //------------ Admin: Force republish ----------------------------------------
1931 
api_republish_all(req: Request) -> RoutingResult1932 async fn api_republish_all(req: Request) -> RoutingResult {
1933     match *req.method() {
1934         Method::POST => aa!(req, Permission::CA_ADMIN, {
1935             render_empty_res(req.state().republish_all().await)
1936         }),
1937         _ => render_unknown_method(),
1938     }
1939 }
1940 
api_resync_all(req: Request) -> RoutingResult1941 async fn api_resync_all(req: Request) -> RoutingResult {
1942     match *req.method() {
1943         Method::POST => aa!(req, Permission::CA_ADMIN, {
1944             let actor = req.actor();
1945             render_empty_res(req.state().cas_repo_sync_all(&actor).await)
1946         }),
1947         _ => render_unknown_method(),
1948     }
1949 }
1950 
1951 /// Refresh all CAs
api_refresh_all(req: Request) -> RoutingResult1952 async fn api_refresh_all(req: Request) -> RoutingResult {
1953     match *req.method() {
1954         Method::POST => aa!(req, Permission::CA_ADMIN, {
1955             let actor = req.actor();
1956             render_empty_res(req.state().cas_refresh_all(&actor).await)
1957         }),
1958         _ => render_unknown_method(),
1959     }
1960 }
1961 
1962 //------------ Serve RRDP Files ----------------------------------------------
1963 
rrdp(req: Request) -> RoutingResult1964 async fn rrdp(req: Request) -> RoutingResult {
1965     if !req.path().full().starts_with("/rrdp/") {
1966         Err(req) // Not for us
1967     } else {
1968         let mut full_path: PathBuf = req.state.rrdp_base_path();
1969         let (_, path) = req.path.remaining().split_at(1);
1970         let cache_seconds = if path.ends_with("notification.xml") { 60 } else { 86400 };
1971         full_path.push(path);
1972 
1973         match File::open(full_path) {
1974             Ok(mut file) => {
1975                 let mut buffer = Vec::new();
1976                 file.read_to_end(&mut buffer).unwrap();
1977 
1978                 Ok(HttpResponse::xml_with_cache(buffer, cache_seconds))
1979             }
1980             _ => Ok(HttpResponse::not_found()),
1981         }
1982     }
1983 }
1984 
1985 //------------ Support Resource Tagged Attestations (RTA) ----------------------
1986 
api_ca_rta(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1987 async fn api_ca_rta(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1988     match path.path_arg() {
1989         Some(name) => match *req.method() {
1990             Method::POST => match path.next() {
1991                 Some("sign") => api_ca_rta_sign(req, ca, name).await,
1992                 Some("multi") => match path.next() {
1993                     Some("prep") => api_ca_rta_multi_prep(req, ca, name).await,
1994                     Some("cosign") => api_ca_rta_multi_sign(req, ca, name).await,
1995                     _ => render_unknown_method(),
1996                 },
1997                 _ => render_unknown_method(),
1998             },
1999             Method::GET => {
2000                 if name.is_empty() {
2001                     api_ca_rta_list(req, ca).await
2002                 } else {
2003                     api_ca_rta_show(req, ca, name).await
2004                 }
2005             }
2006             _ => render_unknown_method(),
2007         },
2008         None => match *req.method() {
2009             Method::GET => api_ca_rta_list(req, ca).await,
2010             _ => render_unknown_method(),
2011         },
2012     }
2013 }
2014 
api_ca_rta_list(req: Request, ca: Handle) -> RoutingResult2015 async fn api_ca_rta_list(req: Request, ca: Handle) -> RoutingResult {
2016     aa!(
2017         req,
2018         Permission::RTA_LIST,
2019         ca.clone(),
2020         render_json_res(req.state().rta_list(ca).await)
2021     )
2022 }
2023 
api_ca_rta_show(req: Request, ca: Handle, name: RtaName) -> RoutingResult2024 async fn api_ca_rta_show(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2025     aa!(
2026         req,
2027         Permission::RTA_READ,
2028         ca.clone(),
2029         render_json_res(req.state().rta_show(ca, name).await)
2030     )
2031 }
2032 
api_ca_rta_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult2033 async fn api_ca_rta_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2034     aa!(req, Permission::RTA_UPDATE, ca.clone(), {
2035         let actor = req.actor();
2036         let state = req.state().clone();
2037         match req.json().await {
2038             Err(e) => render_error(e),
2039             Ok(request) => render_empty_res(state.rta_sign(ca, name, request, &actor).await),
2040         }
2041     })
2042 }
2043 
api_ca_rta_multi_prep(req: Request, ca: Handle, name: RtaName) -> RoutingResult2044 async fn api_ca_rta_multi_prep(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2045     aa!(req, Permission::RTA_UPDATE, ca.clone(), {
2046         let actor = req.actor();
2047         let state = req.state().clone();
2048 
2049         match req.json().await {
2050             Ok(resources) => render_json_res(state.rta_multi_prep(ca, name, resources, &actor).await),
2051             Err(e) => render_error(e),
2052         }
2053     })
2054 }
2055 
api_ca_rta_multi_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult2056 async fn api_ca_rta_multi_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2057     aa!(req, Permission::RTA_UPDATE, ca.clone(), {
2058         let actor = req.actor();
2059         let state = req.state().clone();
2060         match req.json().await {
2061             Ok(rta) => render_empty_res(state.rta_multi_cosign(ca, name, rta, &actor).await),
2062             Err(_) => render_error(Error::custom("Cannot decode RTA for co-signing")),
2063         }
2064     })
2065 }
2066 
2067 //------------ Tests ---------------------------------------------------------
2068 #[cfg(test)]
2069 mod tests {
2070 
2071     // NOTE: This is extensively tested through the functional and e2e tests found under
2072     //       the $project/tests dir
2073     use crate::test;
2074     use std::fs;
2075 
2076     #[tokio::test]
start_krill_daemon()2077     async fn start_krill_daemon() {
2078         let dir = test::start_krill_with_default_test_config(false, false, false).await;
2079         let _ = fs::remove_dir_all(dir);
2080     }
2081 
2082     #[tokio::test]
start_krill_pubd_daemon()2083     async fn start_krill_pubd_daemon() {
2084         let dir = test::start_krill_pubd().await;
2085         let _ = fs::remove_dir_all(dir);
2086     }
2087 }
2088