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