1 //! Helper functions for testing Krill.
2 
3 use std::{
4     fs,
5     fs::File,
6     io::Write,
7     path::{Path, PathBuf},
8     str::FromStr,
9     sync::Arc,
10     time::Duration,
11 };
12 
13 use bytes::Bytes;
14 
15 use hyper::StatusCode;
16 use tokio::time::{sleep, timeout};
17 
18 use rpki::{repository::crypto::KeyIdentifier, uri};
19 
20 use crate::{
21     cli::{
22         options::{BulkCaCommand, CaCommand, Command, Options, PubServerCommand},
23         report::{ApiResponse, ReportFormat},
24         {Error, KrillClient},
25     },
26     commons::{
27         api::{
28             AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, CertAuthInfo,
29             CertAuthInit, CertifiedKeyInfo, ChildHandle, Handle, ObjectName, ParentCaContact, ParentCaReq,
30             ParentHandle, ParentStatuses, PublicationServerUris, PublisherDetails, PublisherHandle, PublisherList,
31             RepositoryContact, ResourceClassKeysInfo, ResourceClassName, ResourceSet, RoaDefinition,
32             RoaDefinitionUpdates, RtaList, RtaName, RtaPrepResponse, TypedPrefix, UpdateChildRequest,
33         },
34         bgp::{Announcement, BgpAnalysisReport, BgpAnalysisSuggestion},
35         crypto::SignSupport,
36         remote::rfc8183,
37         remote::rfc8183::{ChildRequest, RepositoryResponse},
38         util::httpclient,
39     },
40     daemon::{
41         ca::{ta_handle, ResourceTaggedAttestation, RtaContentRequest, RtaPrepareRequest},
42         config::Config,
43         http::server,
44     },
45 };
46 
47 #[cfg(test)]
48 use crate::commons::crypto::IdCert;
49 
50 pub const KRILL_SERVER_URI: &str = "https://localhost:3000/";
51 pub const KRILL_PUBD_SERVER_URI: &str = "https://localhost:3001/";
52 
init_logging()53 pub fn init_logging() {
54     // Just creates a test config so we can initialize logging, then forgets about it
55     let d = PathBuf::from(".");
56     let _ = Config::test(&d, false, false, false).init_logging();
57 }
58 
info(msg: impl std::fmt::Display)59 pub fn info(msg: impl std::fmt::Display) {
60     info!("{}", msg); // we can change this to using the logger crate later
61 }
62 
sleep_seconds(secs: u64)63 pub async fn sleep_seconds(secs: u64) {
64     sleep(Duration::from_secs(secs)).await
65 }
66 
sleep_millis(millis: u64)67 pub async fn sleep_millis(millis: u64) {
68     sleep(Duration::from_millis(millis)).await
69 }
70 
krill_server_ready() -> bool71 pub async fn krill_server_ready() -> bool {
72     server_ready(KRILL_SERVER_URI).await
73 }
74 
krill_pubd_ready() -> bool75 pub async fn krill_pubd_ready() -> bool {
76     server_ready(KRILL_PUBD_SERVER_URI).await
77 }
78 
server_ready(uri: &str) -> bool79 pub async fn server_ready(uri: &str) -> bool {
80     let health = format!("{}health", uri);
81 
82     for _ in 0..300 {
83         match httpclient::client(&health) {
84             Ok(client) => {
85                 let res = timeout(Duration::from_millis(100), client.get(&health).send()).await;
86 
87                 if let Ok(Ok(res)) = res {
88                     if res.status() == StatusCode::OK {
89                         return true;
90                     } else {
91                         eprintln!("Got status: {}", res.status());
92                     }
93                 }
94             }
95             Err(_) => return false,
96         }
97         sleep_millis(100).await;
98     }
99 
100     false
101 }
102 
test_config(dir: &Path, enable_testbed: bool, enable_ca_refresh: bool, enable_suspend: bool) -> Config103 pub fn test_config(dir: &Path, enable_testbed: bool, enable_ca_refresh: bool, enable_suspend: bool) -> Config {
104     if enable_testbed {
105         crate::constants::enable_test_mode();
106         crate::constants::enable_test_announcements();
107     }
108     Config::test(dir, enable_testbed, enable_ca_refresh, enable_suspend)
109 }
110 
init_config(config: &Config)111 pub fn init_config(config: &Config) {
112     if config.init_logging().is_err() {
113         trace!("Logging already initialized");
114     }
115     config.verify().unwrap();
116 }
117 
118 /// Starts krill server for testing using the given configuration. Creates a random base directory in the 'work' folder,
119 /// adjusts the config to use it and returns it. Be sure to clean it up when the test is done.
start_krill_with_custom_config(mut config: Config) -> PathBuf120 pub async fn start_krill_with_custom_config(mut config: Config) -> PathBuf {
121     let dir = tmp_dir();
122     config.set_data_dir(dir.clone());
123     start_krill(config).await;
124     dir
125 }
126 
127 /// Starts krill server for testing using the default test configuration, and optionally with testbed mode enabled.
128 /// Creates a random base directory in the 'work' folder, and returns it. Be sure to clean it up when the test is done.
start_krill_with_default_test_config( enable_testbed: bool, enable_ca_refresh: bool, enable_suspend: bool, ) -> PathBuf129 pub async fn start_krill_with_default_test_config(
130     enable_testbed: bool,
131     enable_ca_refresh: bool,
132     enable_suspend: bool,
133 ) -> PathBuf {
134     let dir = tmp_dir();
135     let config = test_config(&dir, enable_testbed, enable_ca_refresh, enable_suspend);
136     start_krill(config).await;
137     dir
138 }
139 
start_krill(config: Config)140 async fn start_krill(config: Config) {
141     init_config(&config);
142     tokio::spawn(start_krill_with_error_trap(Arc::new(config)));
143     assert!(krill_server_ready().await);
144 }
145 
start_krill_with_error_trap(config: Arc<Config>)146 async fn start_krill_with_error_trap(config: Arc<Config>) {
147     if let Err(err) = server::start_krill_daemon(config).await {
148         error!("Krill failed to start: {}", err);
149     }
150 }
151 
152 /// Starts a krill pubd for testing on its own port, and its
153 /// own temp dir for storage.
start_krill_pubd() -> PathBuf154 pub async fn start_krill_pubd() -> PathBuf {
155     let dir = tmp_dir();
156     let mut config = test_config(&dir, false, false, false);
157     init_config(&config);
158     config.port = 3001;
159 
160     tokio::spawn(start_krill_with_error_trap(Arc::new(config)));
161     assert!(krill_pubd_ready().await);
162 
163     // Initialize the repository using separate URIs
164     let uris = {
165         let rsync_base = uri::Rsync::from_str("rsync://localhost/dedicated-repo/").unwrap();
166         let rrdp_base_uri = uri::Https::from_str("https://localhost:3001/test-rrdp/").unwrap();
167         PublicationServerUris::new(rrdp_base_uri, rsync_base)
168     };
169     let command = PubServerCommand::RepositoryInit(uris);
170     krill_dedicated_pubd_admin(command).await;
171 
172     dir
173 }
174 
krill_admin(command: Command) -> ApiResponse175 pub async fn krill_admin(command: Command) -> ApiResponse {
176     let krillc_opts = Options::new(https(KRILL_SERVER_URI), "secret", ReportFormat::Json, command);
177     match KrillClient::process(krillc_opts).await {
178         Ok(res) => res, // ok
179         Err(e) => panic!("{}", e),
180     }
181 }
182 
krill_embedded_pubd_admin(command: PubServerCommand) -> ApiResponse183 pub async fn krill_embedded_pubd_admin(command: PubServerCommand) -> ApiResponse {
184     krill_admin(Command::PubServer(command)).await
185 }
186 
krill_dedicated_pubd_admin(command: PubServerCommand) -> ApiResponse187 pub async fn krill_dedicated_pubd_admin(command: PubServerCommand) -> ApiResponse {
188     let options = Options::new(
189         https(KRILL_PUBD_SERVER_URI),
190         "secret",
191         ReportFormat::Json,
192         Command::PubServer(command),
193     );
194     match KrillClient::process(options).await {
195         Ok(res) => res, // ok
196         Err(e) => panic!("{}", e),
197     }
198 }
199 
krill_admin_expect_error(command: Command) -> Error200 pub async fn krill_admin_expect_error(command: Command) -> Error {
201     let krillc_opts = Options::new(https(KRILL_SERVER_URI), "secret", ReportFormat::Json, command);
202     match KrillClient::process(krillc_opts).await {
203         Ok(_res) => panic!("Expected error"),
204         Err(e) => e,
205     }
206 }
207 
cas_refresh_all()208 pub async fn cas_refresh_all() {
209     krill_admin(Command::Bulk(BulkCaCommand::Refresh)).await;
210 }
211 
cas_refresh_single(ca: &Handle)212 pub async fn cas_refresh_single(ca: &Handle) {
213     krill_admin(Command::CertAuth(CaCommand::Refresh(ca.clone()))).await;
214 }
215 
ca_suspend_child(ca: &Handle, child: &ChildHandle)216 pub async fn ca_suspend_child(ca: &Handle, child: &ChildHandle) {
217     krill_admin(Command::CertAuth(CaCommand::ChildUpdate(
218         ca.clone(),
219         child.clone(),
220         UpdateChildRequest::suspend(),
221     )))
222     .await;
223 }
224 
ca_unsuspend_child(ca: &Handle, child: &ChildHandle)225 pub async fn ca_unsuspend_child(ca: &Handle, child: &ChildHandle) {
226     krill_admin(Command::CertAuth(CaCommand::ChildUpdate(
227         ca.clone(),
228         child.clone(),
229         UpdateChildRequest::unsuspend(),
230     )))
231     .await;
232 }
233 
init_ca(handle: &Handle)234 pub async fn init_ca(handle: &Handle) {
235     krill_admin(Command::CertAuth(CaCommand::Init(CertAuthInit::new(handle.clone())))).await;
236 }
237 
delete_ca(ca: &Handle)238 pub async fn delete_ca(ca: &Handle) {
239     krill_admin(Command::CertAuth(CaCommand::Delete(ca.clone()))).await;
240 }
241 
ca_repo_update_rfc8181(handle: &Handle, response: RepositoryResponse)242 pub async fn ca_repo_update_rfc8181(handle: &Handle, response: RepositoryResponse) {
243     krill_admin(Command::CertAuth(CaCommand::RepoUpdate(
244         handle.clone(),
245         RepositoryContact::new(response),
246     )))
247     .await;
248 }
249 
generate_new_id(handle: &Handle)250 pub async fn generate_new_id(handle: &Handle) {
251     krill_admin(Command::CertAuth(CaCommand::UpdateId(handle.clone()))).await;
252 }
253 
parent_contact(handle: &Handle, child: &ChildHandle) -> ParentCaContact254 pub async fn parent_contact(handle: &Handle, child: &ChildHandle) -> ParentCaContact {
255     match krill_admin(Command::CertAuth(CaCommand::ParentResponse(
256         handle.clone(),
257         child.clone(),
258     )))
259     .await
260     {
261         ApiResponse::ParentCaContact(contact) => contact,
262         _ => panic!("Expected RFC8183 parent response"),
263     }
264 }
265 
request(handle: &Handle) -> rfc8183::ChildRequest266 pub async fn request(handle: &Handle) -> rfc8183::ChildRequest {
267     match krill_admin(Command::CertAuth(CaCommand::ChildRequest(handle.clone()))).await {
268         ApiResponse::Rfc8183ChildRequest(req) => req,
269         _ => panic!("Expected child request"),
270     }
271 }
272 
add_child_to_ta_rfc6492( handle: &Handle, child_request: rfc8183::ChildRequest, resources: ResourceSet, ) -> ParentCaContact273 pub async fn add_child_to_ta_rfc6492(
274     handle: &Handle,
275     child_request: rfc8183::ChildRequest,
276     resources: ResourceSet,
277 ) -> ParentCaContact {
278     let (_, _, id_cert) = child_request.unpack();
279     let req = AddChildRequest::new(handle.clone(), resources, id_cert);
280     let res = krill_admin(Command::CertAuth(CaCommand::ChildAdd(ta_handle(), req))).await;
281 
282     match res {
283         ApiResponse::ParentCaContact(info) => info,
284         _ => panic!("Expected ParentCaInfo response"),
285     }
286 }
287 
add_child_rfc6492( parent: &ParentHandle, child: &ChildHandle, child_request: rfc8183::ChildRequest, resources: ResourceSet, ) -> ParentCaContact288 pub async fn add_child_rfc6492(
289     parent: &ParentHandle,
290     child: &ChildHandle,
291     child_request: rfc8183::ChildRequest,
292     resources: ResourceSet,
293 ) -> ParentCaContact {
294     let (_, _, id_cert) = child_request.unpack();
295 
296     let add_child_request = AddChildRequest::new(child.clone(), resources, id_cert);
297 
298     match krill_admin(Command::CertAuth(CaCommand::ChildAdd(
299         parent.clone(),
300         add_child_request,
301     )))
302     .await
303     {
304         ApiResponse::ParentCaContact(info) => info,
305         _ => panic!("Expected ParentCaInfo response"),
306     }
307 }
308 
update_child(ca: &Handle, child: &ChildHandle, resources: &ResourceSet)309 pub async fn update_child(ca: &Handle, child: &ChildHandle, resources: &ResourceSet) {
310     let req = UpdateChildRequest::resources(resources.clone());
311     send_child_request(ca, child, req).await
312 }
313 
update_child_id(ca: &Handle, child: &ChildHandle, req: ChildRequest)314 pub async fn update_child_id(ca: &Handle, child: &ChildHandle, req: ChildRequest) {
315     let (_, _, id) = req.unpack();
316     let req = UpdateChildRequest::id_cert(id);
317     send_child_request(ca, child, req).await
318 }
319 
delete_child(ca: &Handle, child: &ChildHandle)320 pub async fn delete_child(ca: &Handle, child: &ChildHandle) {
321     krill_admin(Command::CertAuth(CaCommand::ChildDelete(ca.clone(), child.clone()))).await;
322 }
323 
suspend_inactive_child(ca: &Handle, child: &ChildHandle)324 pub async fn suspend_inactive_child(ca: &Handle, child: &ChildHandle) {
325     let update = UpdateChildRequest::suspend();
326 
327     krill_admin(Command::CertAuth(CaCommand::ChildUpdate(
328         ca.clone(),
329         child.clone(),
330         update,
331     )))
332     .await;
333 }
334 
unsuspend_child(ca: &Handle, child: &ChildHandle)335 pub async fn unsuspend_child(ca: &Handle, child: &ChildHandle) {
336     let update = UpdateChildRequest::unsuspend();
337 
338     krill_admin(Command::CertAuth(CaCommand::ChildUpdate(
339         ca.clone(),
340         child.clone(),
341         update,
342     )))
343     .await;
344 }
345 
send_child_request(ca: &Handle, child: &Handle, req: UpdateChildRequest)346 async fn send_child_request(ca: &Handle, child: &Handle, req: UpdateChildRequest) {
347     match krill_admin(Command::CertAuth(CaCommand::ChildUpdate(
348         ca.clone(),
349         child.clone(),
350         req,
351     )))
352     .await
353     {
354         ApiResponse::Empty => {}
355         _ => error!("Expected empty ok response"),
356     }
357     cas_refresh_all().await;
358 }
359 
add_parent_to_ca(ca: &Handle, parent: ParentCaReq)360 pub async fn add_parent_to_ca(ca: &Handle, parent: ParentCaReq) {
361     krill_admin(Command::CertAuth(CaCommand::AddParent(ca.clone(), parent))).await;
362 }
363 
parent_statuses(ca: &Handle) -> ParentStatuses364 pub async fn parent_statuses(ca: &Handle) -> ParentStatuses {
365     match krill_admin(Command::CertAuth(CaCommand::ParentStatuses(ca.clone()))).await {
366         ApiResponse::ParentStatuses(status) => status,
367         _ => panic!("Expected parent statuses"),
368     }
369 }
370 
update_parent_contact(ca: &Handle, parent: &ParentHandle, contact: ParentCaContact)371 pub async fn update_parent_contact(ca: &Handle, parent: &ParentHandle, contact: ParentCaContact) {
372     let parent_req = ParentCaReq::new(parent.clone(), contact);
373     krill_admin(Command::CertAuth(CaCommand::AddParent(ca.clone(), parent_req))).await;
374 }
375 
delete_parent(ca: &Handle, parent: &ParentHandle)376 pub async fn delete_parent(ca: &Handle, parent: &ParentHandle) {
377     krill_admin(Command::CertAuth(CaCommand::RemoveParent(ca.clone(), parent.clone()))).await;
378 }
379 
ca_route_authorizations_update(handle: &Handle, updates: RoaDefinitionUpdates)380 pub async fn ca_route_authorizations_update(handle: &Handle, updates: RoaDefinitionUpdates) {
381     krill_admin(Command::CertAuth(CaCommand::RouteAuthorizationsUpdate(
382         handle.clone(),
383         updates,
384     )))
385     .await;
386 }
387 
ca_route_authorizations_update_expect_error(handle: &Handle, updates: RoaDefinitionUpdates)388 pub async fn ca_route_authorizations_update_expect_error(handle: &Handle, updates: RoaDefinitionUpdates) {
389     krill_admin_expect_error(Command::CertAuth(CaCommand::RouteAuthorizationsUpdate(
390         handle.clone(),
391         updates,
392     )))
393     .await;
394 }
395 
ca_route_authorizations_suggestions(handle: &Handle) -> BgpAnalysisSuggestion396 pub async fn ca_route_authorizations_suggestions(handle: &Handle) -> BgpAnalysisSuggestion {
397     match krill_admin(Command::CertAuth(CaCommand::BgpAnalysisSuggest(handle.clone(), None))).await {
398         ApiResponse::BgpAnalysisSuggestions(suggestion) => suggestion,
399         _ => panic!("Expected ROA suggestion"),
400     }
401 }
402 
ca_route_authorization_dryrun(handle: &Handle, updates: RoaDefinitionUpdates) -> BgpAnalysisReport403 pub async fn ca_route_authorization_dryrun(handle: &Handle, updates: RoaDefinitionUpdates) -> BgpAnalysisReport {
404     match krill_admin(Command::CertAuth(CaCommand::RouteAuthorizationsDryRunUpdate(
405         handle.clone(),
406         updates,
407     )))
408     .await
409     {
410         ApiResponse::BgpAnalysisFull(report) => report,
411         _ => panic!("Expected BGP analysis report"),
412     }
413 }
414 
ca_aspas_add(handle: &Handle, aspa: AspaDefinition)415 pub async fn ca_aspas_add(handle: &Handle, aspa: AspaDefinition) {
416     krill_admin(Command::CertAuth(CaCommand::AspasAddOrReplace(handle.clone(), aspa))).await;
417 }
418 
ca_aspas_expect(handle: &Handle, expected_aspas: AspaDefinitionList)419 pub async fn ca_aspas_expect(handle: &Handle, expected_aspas: AspaDefinitionList) {
420     let res = krill_admin(Command::CertAuth(CaCommand::AspasList(handle.clone()))).await;
421 
422     if let ApiResponse::AspaDefinitions(found_aspas) = res {
423         if expected_aspas != found_aspas {
424             panic!("Expected ASPAs:\n{}, Got ASPAs:\n{}", expected_aspas, found_aspas)
425         }
426     } else {
427         panic!("Expected AspaDefinitionsList")
428     }
429 }
430 
ca_aspas_update(handle: &Handle, customer: AspaCustomer, update: AspaProvidersUpdate)431 pub async fn ca_aspas_update(handle: &Handle, customer: AspaCustomer, update: AspaProvidersUpdate) {
432     krill_admin(Command::CertAuth(CaCommand::AspasUpdate(
433         handle.clone(),
434         customer,
435         update,
436     )))
437     .await;
438 }
439 
ca_aspas_remove(handle: &Handle, customer: AspaCustomer)440 pub async fn ca_aspas_remove(handle: &Handle, customer: AspaCustomer) {
441     krill_admin(Command::CertAuth(CaCommand::AspasRemove(handle.clone(), customer))).await;
442 }
443 
ca_details(handle: &Handle) -> CertAuthInfo444 pub async fn ca_details(handle: &Handle) -> CertAuthInfo {
445     match krill_admin(Command::CertAuth(CaCommand::Show(handle.clone()))).await {
446         ApiResponse::CertAuthInfo(inf) => inf,
447         _ => panic!("Expected cert auth info"),
448     }
449 }
450 
rta_sign_sign( ca: Handle, name: RtaName, resources: ResourceSet, keys: Vec<KeyIdentifier>, content: Bytes, )451 pub async fn rta_sign_sign(
452     ca: Handle,
453     name: RtaName,
454     resources: ResourceSet,
455     keys: Vec<KeyIdentifier>,
456     content: Bytes,
457 ) {
458     let request = RtaContentRequest::new(resources, SignSupport::sign_validity_days(14), keys, content);
459     let command = Command::CertAuth(CaCommand::RtaSign(ca, name, request));
460     krill_admin(command).await;
461 }
462 
rta_list(ca: Handle) -> RtaList463 pub async fn rta_list(ca: Handle) -> RtaList {
464     let command = Command::CertAuth(CaCommand::RtaList(ca));
465     match krill_admin(command).await {
466         ApiResponse::RtaList(list) => list,
467         _ => panic!("Expected RTA list"),
468     }
469 }
470 
rta_show(ca: Handle, name: RtaName) -> ResourceTaggedAttestation471 pub async fn rta_show(ca: Handle, name: RtaName) -> ResourceTaggedAttestation {
472     let command = Command::CertAuth(CaCommand::RtaShow(ca, name, None));
473     match krill_admin(command).await {
474         ApiResponse::Rta(rta) => rta,
475         _ => panic!("Expected RTA"),
476     }
477 }
478 
rta_multi_prep(ca: Handle, name: RtaName, resources: ResourceSet) -> RtaPrepResponse479 pub async fn rta_multi_prep(ca: Handle, name: RtaName, resources: ResourceSet) -> RtaPrepResponse {
480     let request = RtaPrepareRequest::new(resources, SignSupport::sign_validity_days(14));
481     let command = Command::CertAuth(CaCommand::RtaMultiPrep(ca, name, request));
482     match krill_admin(command).await {
483         ApiResponse::RtaMultiPrep(res) => res,
484         _ => panic!("Expected RtaMultiPrep"),
485     }
486 }
487 
rta_multi_cosign(ca: Handle, name: RtaName, rta: ResourceTaggedAttestation)488 pub async fn rta_multi_cosign(ca: Handle, name: RtaName, rta: ResourceTaggedAttestation) {
489     let command = Command::CertAuth(CaCommand::RtaMultiCoSign(ca, name, rta));
490     krill_admin(command).await;
491 }
492 
ca_key_for_rcn(handle: &Handle, rcn: &ResourceClassName) -> CertifiedKeyInfo493 pub async fn ca_key_for_rcn(handle: &Handle, rcn: &ResourceClassName) -> CertifiedKeyInfo {
494     ca_details(handle)
495         .await
496         .resource_classes()
497         .get(rcn)
498         .unwrap()
499         .current_key()
500         .unwrap()
501         .clone()
502 }
503 
ca_new_key_for_rcn(handle: &Handle, rcn: &ResourceClassName) -> CertifiedKeyInfo504 pub async fn ca_new_key_for_rcn(handle: &Handle, rcn: &ResourceClassName) -> CertifiedKeyInfo {
505     ca_details(handle)
506         .await
507         .resource_classes()
508         .get(rcn)
509         .unwrap()
510         .new_key()
511         .unwrap()
512         .clone()
513 }
514 
ca_contains_resources(handle: &Handle, resources: &ResourceSet) -> bool515 pub async fn ca_contains_resources(handle: &Handle, resources: &ResourceSet) -> bool {
516     for _ in 0..30_u8 {
517         if ca_current_resources(handle).await.contains(resources) {
518             return true;
519         }
520         cas_refresh_all().await;
521         sleep_seconds(1).await
522     }
523     false
524 }
525 
ca_equals_resources(handle: &Handle, resources: &ResourceSet) -> bool526 pub async fn ca_equals_resources(handle: &Handle, resources: &ResourceSet) -> bool {
527     for _ in 0..30_u8 {
528         if &ca_current_resources(handle).await == resources {
529             return true;
530         }
531         cas_refresh_all().await;
532         sleep_seconds(1).await
533     }
534     false
535 }
536 
rc_is_removed(handle: &Handle) -> bool537 pub async fn rc_is_removed(handle: &Handle) -> bool {
538     for _ in 0..300 {
539         let ca = ca_details(handle).await;
540         if ca.resource_classes().get(&ResourceClassName::default()).is_none() {
541             return true;
542         }
543         cas_refresh_all().await;
544         sleep_seconds(100).await
545     }
546     false
547 }
548 
ca_current_resources(handle: &Handle) -> ResourceSet549 pub async fn ca_current_resources(handle: &Handle) -> ResourceSet {
550     let ca = ca_details(handle).await;
551 
552     let mut res = ResourceSet::default();
553 
554     for rc in ca.resource_classes().values() {
555         if let Some(resources) = rc.current_resources() {
556             res = res.union(resources)
557         }
558     }
559 
560     res
561 }
562 
list_publishers() -> PublisherList563 pub async fn list_publishers() -> PublisherList {
564     match krill_embedded_pubd_admin(PubServerCommand::PublisherList).await {
565         ApiResponse::PublisherList(pub_list) => pub_list,
566         _ => panic!("Expected publisher list"),
567     }
568 }
569 
publisher_details(publisher: &PublisherHandle) -> PublisherDetails570 pub async fn publisher_details(publisher: &PublisherHandle) -> PublisherDetails {
571     match krill_embedded_pubd_admin(PubServerCommand::ShowPublisher(publisher.clone())).await {
572         ApiResponse::PublisherDetails(pub_details) => pub_details,
573         _ => panic!("Expected publisher details"),
574     }
575 }
576 
dedicated_repo_publisher_details(publisher: &PublisherHandle) -> PublisherDetails577 pub async fn dedicated_repo_publisher_details(publisher: &PublisherHandle) -> PublisherDetails {
578     match krill_dedicated_pubd_admin(PubServerCommand::ShowPublisher(publisher.clone())).await {
579         ApiResponse::PublisherDetails(pub_details) => pub_details,
580         _ => panic!("Expected publisher details"),
581     }
582 }
583 
publisher_request(handle: &Handle) -> rfc8183::PublisherRequest584 pub async fn publisher_request(handle: &Handle) -> rfc8183::PublisherRequest {
585     match krill_admin(Command::CertAuth(CaCommand::RepoPublisherRequest(handle.clone()))).await {
586         ApiResponse::Rfc8183PublisherRequest(req) => req,
587         _ => panic!("Expected publisher request"),
588     }
589 }
590 
591 /// This method sets up a test directory with a random name (a number)
592 /// under 'work', relative to where cargo is running. It then runs the
593 /// test provided in the closure, and finally it cleans up the test
594 /// directory.
595 ///
596 /// Note that if your test fails the directory is not cleaned up.
test_under_tmp<F>(op: F) where F: FnOnce(PathBuf),597 pub fn test_under_tmp<F>(op: F)
598 where
599     F: FnOnce(PathBuf),
600 {
601     let dir = sub_dir(&PathBuf::from("work"));
602     let path = PathBuf::from(&dir);
603 
604     op(dir);
605 
606     let _result = fs::remove_dir_all(path);
607 }
608 
tmp_dir() -> PathBuf609 pub fn tmp_dir() -> PathBuf {
610     sub_dir(&PathBuf::from("work"))
611 }
612 
613 /// This method sets up a random subdirectory and returns it. It is
614 /// assumed that the caller will clean this directory themselves.
sub_dir(base_dir: &Path) -> PathBuf615 pub fn sub_dir(base_dir: &Path) -> PathBuf {
616     let mut bytes = [0; 8];
617     openssl::rand::rand_bytes(&mut bytes).unwrap();
618 
619     let mut dir = base_dir.to_path_buf();
620     dir.push(hex::encode(bytes));
621 
622     let full_path = PathBuf::from(&dir);
623     fs::create_dir_all(&full_path).unwrap();
624 
625     full_path
626 }
627 
rsync(s: &str) -> uri::Rsync628 pub fn rsync(s: &str) -> uri::Rsync {
629     uri::Rsync::from_str(s).unwrap()
630 }
631 
https(s: &str) -> uri::Https632 pub fn https(s: &str) -> uri::Https {
633     uri::Https::from_str(s).unwrap()
634 }
635 
handle(s: &str) -> Handle636 pub fn handle(s: &str) -> Handle {
637     Handle::from_str(s).unwrap()
638 }
639 
ipv4_resources(v4: &str) -> ResourceSet640 pub fn ipv4_resources(v4: &str) -> ResourceSet {
641     ResourceSet::from_strs("", v4, "").unwrap()
642 }
643 
resources(asn: &str, v4: &str, v6: &str) -> ResourceSet644 pub fn resources(asn: &str, v4: &str, v6: &str) -> ResourceSet {
645     ResourceSet::from_strs(asn, v4, v6).unwrap()
646 }
647 
rcn(nr: u32) -> ResourceClassName648 pub fn rcn(nr: u32) -> ResourceClassName {
649     ResourceClassName::from(nr)
650 }
651 
as_bytes(s: &str) -> Bytes652 pub fn as_bytes(s: &str) -> Bytes {
653     Bytes::copy_from_slice(s.as_bytes())
654 }
655 
save_file(base_dir: &Path, file_name: &str, content: &[u8])656 pub fn save_file(base_dir: &Path, file_name: &str, content: &[u8]) {
657     let mut full_name = base_dir.to_path_buf();
658     full_name.push(PathBuf::from(file_name));
659     let mut f = File::create(full_name).unwrap();
660     f.write_all(content).unwrap();
661 }
662 
663 // Support testing announcements and ROAs etc
664 
announcement(s: &str) -> Announcement665 pub fn announcement(s: &str) -> Announcement {
666     let def = definition(s);
667     Announcement::from(def)
668 }
669 
definition(s: &str) -> RoaDefinition670 pub fn definition(s: &str) -> RoaDefinition {
671     RoaDefinition::from_str(s).unwrap()
672 }
673 
typed_prefix(s: &str) -> TypedPrefix674 pub fn typed_prefix(s: &str) -> TypedPrefix {
675     TypedPrefix::from_str(s).unwrap()
676 }
677 
repo_update(ca: &Handle, contact: RepositoryContact)678 pub async fn repo_update(ca: &Handle, contact: RepositoryContact) {
679     let command = Command::CertAuth(CaCommand::RepoUpdate(ca.clone(), contact));
680     krill_admin(command).await;
681 }
682 
embedded_repository_response(publisher: &PublisherHandle) -> rfc8183::RepositoryResponse683 pub async fn embedded_repository_response(publisher: &PublisherHandle) -> rfc8183::RepositoryResponse {
684     let command = PubServerCommand::RepositoryResponse(publisher.clone());
685     match krill_embedded_pubd_admin(command).await {
686         ApiResponse::Rfc8183RepositoryResponse(response) => response,
687         _ => panic!("Expected repository response."),
688     }
689 }
690 
embedded_repo_add_publisher(req: rfc8183::PublisherRequest)691 pub async fn embedded_repo_add_publisher(req: rfc8183::PublisherRequest) {
692     let command = PubServerCommand::AddPublisher(req);
693     krill_embedded_pubd_admin(command).await;
694 }
695 
dedicated_repository_response(publisher: &PublisherHandle) -> rfc8183::RepositoryResponse696 pub async fn dedicated_repository_response(publisher: &PublisherHandle) -> rfc8183::RepositoryResponse {
697     let command = PubServerCommand::RepositoryResponse(publisher.clone());
698     match krill_dedicated_pubd_admin(command).await {
699         ApiResponse::Rfc8183RepositoryResponse(response) => response,
700         _ => panic!("Expected repository response."),
701     }
702 }
703 
dedicated_repo_add_publisher(req: rfc8183::PublisherRequest)704 pub async fn dedicated_repo_add_publisher(req: rfc8183::PublisherRequest) {
705     let command = PubServerCommand::AddPublisher(req);
706     krill_dedicated_pubd_admin(command).await;
707 }
708 
set_up_ca_with_repo(ca: &Handle)709 pub async fn set_up_ca_with_repo(ca: &Handle) {
710     init_ca(ca).await;
711 
712     // Add the CA as a publisher
713     let publisher_request = publisher_request(ca).await;
714     embedded_repo_add_publisher(publisher_request).await;
715 
716     // Get a Repository Response for the CA
717     let response = embedded_repository_response(ca).await;
718 
719     // Update the repo for the child
720     let contact = RepositoryContact::new(response);
721     repo_update(ca, contact).await;
722 }
723 
expected_mft_and_crl(ca: &Handle, rcn: &ResourceClassName) -> Vec<String>724 pub async fn expected_mft_and_crl(ca: &Handle, rcn: &ResourceClassName) -> Vec<String> {
725     let rc_key = ca_key_for_rcn(ca, rcn).await;
726     let mft_file = rc_key.incoming_cert().mft_name().to_string();
727     let crl_file = rc_key.incoming_cert().crl_name().to_string();
728     vec![mft_file, crl_file]
729 }
730 
expected_new_key_mft_and_crl(ca: &Handle, rcn: &ResourceClassName) -> Vec<String>731 pub async fn expected_new_key_mft_and_crl(ca: &Handle, rcn: &ResourceClassName) -> Vec<String> {
732     let rc_key = ca_new_key_for_rcn(ca, rcn).await;
733     let mft_file = rc_key.incoming_cert().mft_name().to_string();
734     let crl_file = rc_key.incoming_cert().crl_name().to_string();
735     vec![mft_file, crl_file]
736 }
737 
expected_issued_cer(ca: &Handle, rcn: &ResourceClassName) -> String738 pub async fn expected_issued_cer(ca: &Handle, rcn: &ResourceClassName) -> String {
739     let rc_key = ca_key_for_rcn(ca, rcn).await;
740     ObjectName::from(rc_key.incoming_cert().cert()).to_string()
741 }
742 
will_publish_embedded(test_msg: &str, publisher: &PublisherHandle, files: &[String]) -> bool743 pub async fn will_publish_embedded(test_msg: &str, publisher: &PublisherHandle, files: &[String]) -> bool {
744     will_publish(test_msg, publisher, files, PubServer::Embedded).await
745 }
746 
will_publish_dedicated(test_msg: &str, publisher: &PublisherHandle, files: &[String]) -> bool747 pub async fn will_publish_dedicated(test_msg: &str, publisher: &PublisherHandle, files: &[String]) -> bool {
748     will_publish(test_msg, publisher, files, PubServer::Dedicated).await
749 }
750 
751 enum PubServer {
752     Embedded,
753     Dedicated,
754 }
755 
will_publish(test_msg: &str, publisher: &PublisherHandle, files: &[String], server: PubServer) -> bool756 async fn will_publish(test_msg: &str, publisher: &PublisherHandle, files: &[String], server: PubServer) -> bool {
757     let objects: Vec<_> = files.iter().map(|s| s.as_str()).collect();
758     for _ in 0..6000 {
759         let details = {
760             match &server {
761                 PubServer::Dedicated => dedicated_repo_publisher_details(publisher).await,
762                 PubServer::Embedded => publisher_details(publisher).await,
763             }
764         };
765 
766         let current_files = details.current_files();
767 
768         if current_files.len() == objects.len() {
769             let current_files: Vec<&uri::Rsync> = current_files.iter().map(|p| p.uri()).collect();
770             let mut all_matched = true;
771             for o in &objects {
772                 if !current_files.iter().any(|uri| uri.ends_with(o)) {
773                     all_matched = false;
774                 }
775             }
776             if all_matched {
777                 return true;
778             }
779         }
780 
781         sleep_millis(100).await;
782     }
783 
784     let details = publisher_details(publisher).await;
785 
786     eprintln!(
787         "Did not find match for test: {}, for publisher: {}",
788         test_msg, publisher
789     );
790     eprintln!("Found:");
791     for file in details.current_files() {
792         eprintln!("  {}", file.uri());
793     }
794     eprintln!("Expected:");
795     for file in objects {
796         eprintln!("  {}", file);
797     }
798 
799     false
800 }
801 
set_up_ca_under_parent_with_resources(ca: &Handle, parent: &ParentHandle, resources: &ResourceSet)802 pub async fn set_up_ca_under_parent_with_resources(ca: &Handle, parent: &ParentHandle, resources: &ResourceSet) {
803     let child_request = request(ca).await;
804     let parent = {
805         let contact = add_child_rfc6492(parent, ca, child_request, resources.clone()).await;
806         ParentCaReq::new(parent.clone(), contact)
807     };
808     add_parent_to_ca(ca, parent).await;
809     assert!(ca_contains_resources(ca, resources).await);
810 }
811 
ca_roll_init(handle: &Handle)812 pub async fn ca_roll_init(handle: &Handle) {
813     krill_admin(Command::CertAuth(CaCommand::KeyRollInit(handle.clone()))).await;
814 }
815 
ca_roll_activate(handle: &Handle)816 pub async fn ca_roll_activate(handle: &Handle) {
817     krill_admin(Command::CertAuth(CaCommand::KeyRollActivate(handle.clone()))).await;
818 }
819 
state_becomes_new_key(handle: &Handle) -> bool820 pub async fn state_becomes_new_key(handle: &Handle) -> bool {
821     for _ in 0..30_u8 {
822         let ca = ca_details(handle).await;
823 
824         // wait for ALL RCs to become state new key
825         let rc_map = ca.resource_classes();
826 
827         let expected = rc_map.len();
828         let mut found = 0;
829 
830         for rc in rc_map.values() {
831             if let ResourceClassKeysInfo::RollNew(_) = rc.keys() {
832                 found += 1;
833             }
834         }
835 
836         if found == expected {
837             return true;
838         }
839 
840         sleep_seconds(1).await
841     }
842     false
843 }
844 
state_becomes_active(handle: &Handle) -> bool845 pub async fn state_becomes_active(handle: &Handle) -> bool {
846     for _ in 0..300 {
847         let ca = ca_details(handle).await;
848 
849         // wait for ALL RCs to become state active key
850         let rc_map = ca.resource_classes();
851 
852         let expected = rc_map.len();
853         let mut found = 0;
854 
855         for rc in rc_map.values() {
856             if let ResourceClassKeysInfo::Active(_) = rc.keys() {
857                 found += 1;
858             }
859         }
860 
861         if found == expected {
862             return true;
863         }
864 
865         sleep_millis(100).await
866     }
867     false
868 }
869 
870 #[cfg(test)]
test_id_certificate() -> IdCert871 pub fn test_id_certificate() -> IdCert {
872     let data = include_bytes!("../test-resources/oob/id_publisher_ta.cer");
873     IdCert::decode(Bytes::from_static(data)).unwrap()
874 }
875