1#![doc = include_str!("../README.md")]
2
3use std::fs::File;
4use std::path::PathBuf;
5
6use chrono::Utc;
7use nethsm_sdk_rs::ureq::get;
8use rstest::fixture;
9pub use rustainers::Container;
11use rustainers::runner::{RunOption, Runner};
12use testresult::TestResult;
13
14use crate::{
15 Connection,
16 ConnectionSecurity,
17 Credentials,
18 KeyId,
19 KeyMechanism,
20 KeyType,
21 NetHsm,
22 Passphrase,
23 Url,
24 UserRole,
25};
26
27pub static ADMIN_USER_ID: &str = "admin";
29
30pub static ADMIN_USER_PASSPHRASE: &str = "just-an-admin-passphrase";
32
33pub static UNLOCK_PASSPHRASE: &str = "just-an-unlock-passphrase";
35
36pub static DEFAULT_OPERATOR_USER_ID: &str = "operator1";
38
39pub static DEFAULT_OPERATOR_USER_REAL_NAME: &str = "Some Operator";
41
42pub static DEFAULT_OPERATOR_USER_PASSPHRASE: &str = "just-an-operator-passphrase";
44
45pub static OTHER_OPERATOR_USER_ID: &str = "operator2";
47
48pub static OTHER_OPERATOR_USER_REAL_NAME: &str = "Some Other Operator";
50
51pub static OTHER_OPERATOR_USER_PASSPHRASE: &str = "just-another-operator-passphrase";
53
54pub static BACKUP_USER_ID: &str = "backup1";
56
57pub static BACKUP_USER_REAL_NAME: &str = "Some Backup";
59
60pub static BACKUP_USER_PASSPHRASE: &str = "just-a-backup-passphrase";
62
63pub static METRICS_USER_ID: &str = "metrics1";
65
66pub static METRICS_USER_REAL_NAME: &str = "Some Metrics";
68
69pub static METRICS_USER_PASSPHRASE: &str = "just-a-metrics-passphrase";
71
72pub static DEFAULT_RSA_BITS: u32 = 2048;
74
75pub static DEFAULT_KEY_ID: &str = "key1";
77
78pub static OTHER_KEY_ID: &str = "key2";
80
81pub static DEFAULT_TAG: &str = "tag1";
83
84pub static OTHER_TAG: &str = "tag2";
86
87pub static ENC_KEY_ID: &str = "enckey1";
89
90pub static ENC_TAG: &str = "enctag1";
92
93pub static ENC_OPERATOR_USER_ID: &str = "encoperator1";
95
96pub static ENC_OPERATOR_USER_REAL_NAME: &str = "Some Encryption Operator";
98
99pub static ENC_OPERATOR_USER_PASSPHRASE: &str = "just-an-encryption-passphrase";
101
102pub static DEFAULT_AES_BITS: u32 = 128;
104
105pub static NAMESPACE1: &str = "namespace1";
107
108pub static NAMESPACE1_ADMIN_USER_ID: &str = "namespace1~admin";
110
111pub static NAMESPACE1_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace-admin-passphrase";
113
114pub static NAMESPACE1_ADMIN_REAL_NAME: &str = "Namespace1 Admin";
116
117pub static NAMESPACE1_OPERATOR_USER_ID: &str = "namespace1~operator";
119
120pub static NAMESPACE1_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace-operator-passphrase";
122
123pub static NAMESPACE1_OPERATOR_REAL_NAME: &str = "Namespace1 Operator";
125
126pub static NAMESPACE2: &str = "namespace2";
128
129pub static NAMESPACE2_ADMIN_USER_ID: &str = "namespace2~admin";
131
132pub static NAMESPACE2_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace2-admin-passphrase";
134
135pub static NAMESPACE2_ADMIN_REAL_NAME: &str = "Namespace2 Admin";
137
138pub static NAMESPACE2_OPERATOR_USER_ID: &str = "namespace2~operator";
140
141pub static NAMESPACE2_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace2-operator-passphrase";
143
144pub static NAMESPACE2_OPERATOR_REAL_NAME: &str = "Namespace2 Operator";
146
147mod container;
148pub use container::NetHsmImage;
149
150pub async fn create_container() -> TestResult<Container<NetHsmImage>> {
152 let runner = Runner::podman()?;
153 let image = NetHsmImage::default();
154 println!("image: {:#?}", image.image);
155 let run_options = RunOption::builder().with_remove(true).build();
156 let container = runner.start_with_options(image, run_options).await?;
157 println!("serving URL: {}", container.url().await?);
158 Ok(container)
159}
160
161pub fn create_nethsm(url: Url) -> TestResult<NetHsm> {
163 Ok(NetHsm::new(
164 Connection::new(url, ConnectionSecurity::Unsafe),
165 Some(Credentials::new(
166 ADMIN_USER_ID.parse()?,
167 Some(Passphrase::new(ADMIN_USER_PASSPHRASE.to_string())),
168 )),
169 None,
170 None,
171 )?)
172}
173
174#[fixture]
176pub async fn unprovisioned_nethsm() -> TestResult<(NetHsm, rustainers::Container<NetHsmImage>)> {
177 let container = create_container().await?;
178
179 Ok((create_nethsm(container.url().await?)?, container))
180}
181
182fn provision_nethsm(nethsm: &NetHsm) -> TestResult {
183 nethsm.provision(
184 Passphrase::new(UNLOCK_PASSPHRASE.to_string()),
185 Passphrase::new(ADMIN_USER_PASSPHRASE.to_string()),
186 Utc::now(),
187 )?;
188 Ok(())
189}
190
191fn add_users_to_nethsm(nethsm: &NetHsm) -> TestResult {
192 let users = [
193 (
194 UserRole::Operator,
195 DEFAULT_OPERATOR_USER_ID,
196 DEFAULT_OPERATOR_USER_PASSPHRASE,
197 DEFAULT_OPERATOR_USER_REAL_NAME,
198 ),
199 (
200 UserRole::Operator,
201 OTHER_OPERATOR_USER_ID,
202 OTHER_OPERATOR_USER_PASSPHRASE,
203 OTHER_OPERATOR_USER_REAL_NAME,
204 ),
205 (
206 UserRole::Operator,
207 ENC_OPERATOR_USER_ID,
208 ENC_OPERATOR_USER_PASSPHRASE,
209 ENC_OPERATOR_USER_REAL_NAME,
210 ),
211 (
212 UserRole::Metrics,
213 METRICS_USER_ID,
214 METRICS_USER_PASSPHRASE,
215 METRICS_USER_REAL_NAME,
216 ),
217 (
218 UserRole::Backup,
219 BACKUP_USER_ID,
220 BACKUP_USER_PASSPHRASE,
221 BACKUP_USER_REAL_NAME,
222 ),
223 (
224 UserRole::Administrator,
225 NAMESPACE1_ADMIN_USER_ID,
226 NAMESPACE1_ADMIN_USER_PASSPHRASE,
227 NAMESPACE1_ADMIN_REAL_NAME,
228 ),
229 (
230 UserRole::Operator,
231 NAMESPACE1_OPERATOR_USER_ID,
232 NAMESPACE1_OPERATOR_USER_PASSPHRASE,
233 NAMESPACE1_OPERATOR_REAL_NAME,
234 ),
235 (
236 UserRole::Administrator,
237 NAMESPACE2_ADMIN_USER_ID,
238 NAMESPACE2_ADMIN_USER_PASSPHRASE,
239 NAMESPACE2_ADMIN_REAL_NAME,
240 ),
241 (
242 UserRole::Operator,
243 NAMESPACE2_OPERATOR_USER_ID,
244 NAMESPACE2_OPERATOR_USER_PASSPHRASE,
245 NAMESPACE2_OPERATOR_REAL_NAME,
246 ),
247 ];
248
249 println!("Adding users to NetHSM...");
250 for (role, user_id, passphrase, real_name) in users.into_iter() {
251 println!("Adding user: {user_id}");
252 nethsm.add_user(
253 real_name.to_string(),
254 role,
255 Passphrase::new(passphrase.to_string()),
256 Some(user_id.parse()?),
257 )?;
258 }
259 println!("users: {:?}", nethsm.get_users()?);
260 println!("Creating namespaces...");
261 for namespace in [NAMESPACE1, NAMESPACE2] {
262 println!("Creating namespace: {namespace}");
263 nethsm.add_namespace(&namespace.parse()?)?;
264 }
265 println!("namespaces: {:?}", nethsm.get_namespaces()?);
266 Ok(())
267}
268
269fn add_keys_to_nethsm(nethsm: &NetHsm) -> TestResult {
270 let keys = [
271 (
272 vec![KeyMechanism::EdDsaSignature],
273 KeyType::Curve25519,
274 None,
275 DEFAULT_KEY_ID,
276 DEFAULT_TAG,
277 DEFAULT_OPERATOR_USER_ID,
278 ),
279 (
280 vec![
281 KeyMechanism::RsaSignaturePkcs1,
282 KeyMechanism::RsaDecryptionPkcs1,
283 ],
284 KeyType::Rsa,
285 Some(DEFAULT_RSA_BITS),
286 OTHER_KEY_ID,
287 OTHER_TAG,
288 OTHER_OPERATOR_USER_ID,
289 ),
290 (
291 vec![
292 KeyMechanism::AesDecryptionCbc,
293 KeyMechanism::AesEncryptionCbc,
294 ],
295 KeyType::Generic,
296 Some(DEFAULT_AES_BITS),
297 ENC_KEY_ID,
298 ENC_TAG,
299 ENC_OPERATOR_USER_ID,
300 ),
301 ];
302
303 println!("Adding keys to NetHSM...");
304 for (mechanisms, key_type, length, key_id, tag, user_id) in keys {
305 let key_id: &KeyId = &key_id.parse()?;
306 nethsm.generate_key(
307 key_type,
308 mechanisms,
309 length,
310 Some((*key_id).clone()),
311 None,
312 None,
313 )?;
314 nethsm.add_key_tag(key_id, tag)?;
315 nethsm.add_user_tag(&user_id.parse()?, tag)?;
316 if key_type != KeyType::Generic {
318 nethsm.import_key_certificate(key_id, nethsm.get_public_key(key_id)?.into_bytes())?;
319 }
320 }
321
322 println!("users: {:?}", nethsm.get_users()?);
323 println!("keys: {:?}", nethsm.get_keys(None, None)?);
324 Ok(())
325}
326
327#[fixture]
329pub async fn provisioned_nethsm() -> TestResult<(NetHsm, Container<NetHsmImage>)> {
330 let container = create_container().await?;
331 let nethsm = create_nethsm(container.url().await?)?;
332 println!("Provisioning container...");
333 provision_nethsm(&nethsm)?;
334
335 Ok((nethsm, container))
336}
337
338#[fixture]
340pub async fn nethsm_with_users() -> TestResult<(NetHsm, Container<NetHsmImage>)> {
341 let container = create_container().await?;
342 let nethsm = create_nethsm(container.url().await?)?;
343 println!("Provisioning container...");
344 provision_nethsm(&nethsm)?;
345 println!("Adding users to container...");
346 add_users_to_nethsm(&nethsm)?;
347
348 Ok((nethsm, container))
349}
350
351#[fixture]
353pub async fn nethsm_with_keys(
354 #[future] provisioned_nethsm: TestResult<(NetHsm, Container<NetHsmImage>)>,
355) -> TestResult<(NetHsm, Container<NetHsmImage>)> {
356 let (nethsm, container) = provisioned_nethsm.await?;
357
358 println!("Adding users and keys to container...");
359 add_users_to_nethsm(&nethsm)?;
360 add_keys_to_nethsm(&nethsm)?;
361
362 Ok((nethsm, container))
363}
364
365#[fixture]
367pub fn update_file() -> TestResult<PathBuf> {
368 let file_name = "update.img.bin";
369 let update_link =
370 format!("https://raw.githubusercontent.com/Nitrokey/nethsm-sdk-py/main/tests/{file_name}");
371 let download_dir = PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("/tmp".into()));
372 let file = download_dir.join(file_name);
373
374 if !file.exists() {
375 let mut file_bytes = get(&update_link).call()?.into_body().into_reader();
376 let mut file_writer = File::create(&file)?;
377 std::io::copy(&mut file_bytes, &mut file_writer)?;
378 assert!(file.exists());
379 }
380
381 println!("Update file downloaded: {file:?}");
382 Ok(file)
383}