1#![doc = include_str!("../README.md")]
2
3use std::fs::File;
4use std::path::PathBuf;
5
6use chrono::Utc;
7use nethsm::{
8 Connection,
9 ConnectionSecurity,
10 Credentials,
11 KeyId,
12 KeyMechanism,
13 KeyType,
14 NetHsm,
15 Passphrase,
16 Url,
17 UserRole,
18};
19use rstest::fixture;
20pub use rustainers::Container;
22use rustainers::runner::Runner;
23use testresult::TestResult;
24
25pub static ADMIN_USER_ID: &str = "admin";
27
28pub static ADMIN_USER_PASSPHRASE: &str = "just-an-admin-passphrase";
30
31pub static UNLOCK_PASSPHRASE: &str = "just-an-unlock-passphrase";
33
34pub static DEFAULT_OPERATOR_USER_ID: &str = "operator1";
36
37pub static DEFAULT_OPERATOR_USER_REAL_NAME: &str = "Some Operator";
39
40pub static DEFAULT_OPERATOR_USER_PASSPHRASE: &str = "just-an-operator-passphrase";
42
43pub static OTHER_OPERATOR_USER_ID: &str = "operator2";
45
46pub static OTHER_OPERATOR_USER_REAL_NAME: &str = "Some Other Operator";
48
49pub static OTHER_OPERATOR_USER_PASSPHRASE: &str = "just-another-operator-passphrase";
51
52pub static BACKUP_USER_ID: &str = "backup1";
54
55pub static BACKUP_USER_REAL_NAME: &str = "Some Backup";
57
58pub static BACKUP_USER_PASSPHRASE: &str = "just-a-backup-passphrase";
60
61pub static METRICS_USER_ID: &str = "metrics1";
63
64pub static METRICS_USER_REAL_NAME: &str = "Some Metrics";
66
67pub static METRICS_USER_PASSPHRASE: &str = "just-a-metrics-passphrase";
69
70pub static DEFAULT_RSA_BITS: u32 = 2048;
72
73pub static DEFAULT_KEY_ID: &str = "key1";
75
76pub static OTHER_KEY_ID: &str = "key2";
78
79pub static DEFAULT_TAG: &str = "tag1";
81
82pub static OTHER_TAG: &str = "tag2";
84
85pub static ENC_KEY_ID: &str = "enckey1";
87
88pub static ENC_TAG: &str = "enctag1";
90
91pub static ENC_OPERATOR_USER_ID: &str = "encoperator1";
93
94pub static ENC_OPERATOR_USER_REAL_NAME: &str = "Some Encryption Operator";
96
97pub static ENC_OPERATOR_USER_PASSPHRASE: &str = "just-an-encryption-passphrase";
99
100pub static DEFAULT_AES_BITS: u32 = 128;
102
103pub static NAMESPACE1: &str = "namespace1";
105
106pub static NAMESPACE1_ADMIN_USER_ID: &str = "namespace1~admin";
108
109pub static NAMESPACE1_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace-admin-passphrase";
111
112pub static NAMESPACE1_ADMIN_REAL_NAME: &str = "Namespace1 Admin";
114
115pub static NAMESPACE1_OPERATOR_USER_ID: &str = "namespace1~operator";
117
118pub static NAMESPACE1_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace-operator-passphrase";
120
121pub static NAMESPACE1_OPERATOR_REAL_NAME: &str = "Namespace1 Operator";
123
124pub static NAMESPACE2: &str = "namespace2";
126
127pub static NAMESPACE2_ADMIN_USER_ID: &str = "namespace2~admin";
129
130pub static NAMESPACE2_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace2-admin-passphrase";
132
133pub static NAMESPACE2_ADMIN_REAL_NAME: &str = "Namespace2 Admin";
135
136pub static NAMESPACE2_OPERATOR_USER_ID: &str = "namespace2~operator";
138
139pub static NAMESPACE2_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace2-operator-passphrase";
141
142pub static NAMESPACE2_OPERATOR_REAL_NAME: &str = "Namespace2 Operator";
144
145mod container;
146pub use container::NetHsmImage;
147
148pub async fn create_container() -> TestResult<Container<NetHsmImage>> {
150 let runner = Runner::podman()?;
151 let image = NetHsmImage::default();
152 println!("image: {:#?}", image.image);
153 let container = runner.start(image).await?;
154 println!("serving URL: {}", container.url().await?);
155 Ok(container)
156}
157
158pub fn create_nethsm(url: Url) -> TestResult<NetHsm> {
160 Ok(NetHsm::new(
161 Connection::new(url, ConnectionSecurity::Unsafe),
162 Some(Credentials::new(
163 ADMIN_USER_ID.parse()?,
164 Some(Passphrase::new(ADMIN_USER_PASSPHRASE.to_string())),
165 )),
166 None,
167 None,
168 )?)
169}
170
171#[fixture]
173pub async fn unprovisioned_nethsm() -> TestResult<(NetHsm, rustainers::Container<NetHsmImage>)> {
174 let container = create_container().await?;
175
176 Ok((create_nethsm(container.url().await?)?, container))
177}
178
179fn provision_nethsm(nethsm: &NetHsm) -> TestResult {
180 nethsm.provision(
181 Passphrase::new(UNLOCK_PASSPHRASE.to_string()),
182 Passphrase::new(ADMIN_USER_PASSPHRASE.to_string()),
183 Utc::now(),
184 )?;
185 Ok(())
186}
187
188fn add_users_to_nethsm(nethsm: &NetHsm) -> TestResult {
189 let users = [
190 (
191 UserRole::Operator,
192 DEFAULT_OPERATOR_USER_ID,
193 DEFAULT_OPERATOR_USER_PASSPHRASE,
194 DEFAULT_OPERATOR_USER_REAL_NAME,
195 ),
196 (
197 UserRole::Operator,
198 OTHER_OPERATOR_USER_ID,
199 OTHER_OPERATOR_USER_PASSPHRASE,
200 OTHER_OPERATOR_USER_REAL_NAME,
201 ),
202 (
203 UserRole::Operator,
204 ENC_OPERATOR_USER_ID,
205 ENC_OPERATOR_USER_PASSPHRASE,
206 ENC_OPERATOR_USER_REAL_NAME,
207 ),
208 (
209 UserRole::Metrics,
210 METRICS_USER_ID,
211 METRICS_USER_PASSPHRASE,
212 METRICS_USER_REAL_NAME,
213 ),
214 (
215 UserRole::Backup,
216 BACKUP_USER_ID,
217 BACKUP_USER_PASSPHRASE,
218 BACKUP_USER_REAL_NAME,
219 ),
220 (
221 UserRole::Administrator,
222 NAMESPACE1_ADMIN_USER_ID,
223 NAMESPACE1_ADMIN_USER_PASSPHRASE,
224 NAMESPACE1_ADMIN_REAL_NAME,
225 ),
226 (
227 UserRole::Operator,
228 NAMESPACE1_OPERATOR_USER_ID,
229 NAMESPACE1_OPERATOR_USER_PASSPHRASE,
230 NAMESPACE1_OPERATOR_REAL_NAME,
231 ),
232 (
233 UserRole::Administrator,
234 NAMESPACE2_ADMIN_USER_ID,
235 NAMESPACE2_ADMIN_USER_PASSPHRASE,
236 NAMESPACE2_ADMIN_REAL_NAME,
237 ),
238 (
239 UserRole::Operator,
240 NAMESPACE2_OPERATOR_USER_ID,
241 NAMESPACE2_OPERATOR_USER_PASSPHRASE,
242 NAMESPACE2_OPERATOR_REAL_NAME,
243 ),
244 ];
245
246 println!("Adding users to NetHSM...");
247 for (role, user_id, passphrase, real_name) in users.into_iter() {
248 println!("Adding user: {}", user_id);
249 nethsm.add_user(
250 real_name.to_string(),
251 role,
252 Passphrase::new(passphrase.to_string()),
253 Some(user_id.parse()?),
254 )?;
255 }
256 println!("users: {:?}", nethsm.get_users()?);
257 println!("Creating namespaces...");
258 for namespace in [NAMESPACE1, NAMESPACE2] {
259 println!("Creating namespace: {}", namespace);
260 nethsm.add_namespace(&namespace.parse()?)?;
261 }
262 println!("namespaces: {:?}", nethsm.get_namespaces()?);
263 Ok(())
264}
265
266fn add_keys_to_nethsm(nethsm: &NetHsm) -> TestResult {
267 let keys = [
268 (
269 vec![KeyMechanism::EdDsaSignature],
270 KeyType::Curve25519,
271 None,
272 DEFAULT_KEY_ID,
273 DEFAULT_TAG,
274 DEFAULT_OPERATOR_USER_ID,
275 ),
276 (
277 vec![
278 KeyMechanism::RsaSignaturePkcs1,
279 KeyMechanism::RsaDecryptionPkcs1,
280 ],
281 KeyType::Rsa,
282 Some(DEFAULT_RSA_BITS),
283 OTHER_KEY_ID,
284 OTHER_TAG,
285 OTHER_OPERATOR_USER_ID,
286 ),
287 (
288 vec![
289 KeyMechanism::AesDecryptionCbc,
290 KeyMechanism::AesEncryptionCbc,
291 ],
292 KeyType::Generic,
293 Some(DEFAULT_AES_BITS),
294 ENC_KEY_ID,
295 ENC_TAG,
296 ENC_OPERATOR_USER_ID,
297 ),
298 ];
299
300 println!("Adding keys to NetHSM...");
301 for (mechanisms, key_type, length, key_id, tag, user_id) in keys {
302 let key_id: &KeyId = &key_id.parse()?;
303 nethsm.generate_key(key_type, mechanisms, length, Some((*key_id).clone()), None)?;
304 nethsm.add_key_tag(key_id, tag)?;
305 nethsm.add_user_tag(&user_id.parse()?, tag)?;
306 if key_type != KeyType::Generic {
308 nethsm.import_key_certificate(key_id, nethsm.get_public_key(key_id)?.into_bytes())?;
309 }
310 }
311
312 println!("users: {:?}", nethsm.get_users()?);
313 println!("keys: {:?}", nethsm.get_keys(None)?);
314 Ok(())
315}
316
317#[fixture]
319pub async fn provisioned_nethsm() -> TestResult<(NetHsm, Container<NetHsmImage>)> {
320 let container = create_container().await?;
321 let nethsm = create_nethsm(container.url().await?)?;
322 println!("Provisioning container...");
323 provision_nethsm(&nethsm)?;
324
325 Ok((nethsm, container))
326}
327
328#[fixture]
330pub async fn nethsm_with_users() -> TestResult<(NetHsm, Container<NetHsmImage>)> {
331 let container = create_container().await?;
332 let nethsm = create_nethsm(container.url().await?)?;
333 println!("Provisioning container...");
334 provision_nethsm(&nethsm)?;
335 println!("Adding users to container...");
336 add_users_to_nethsm(&nethsm)?;
337
338 Ok((nethsm, container))
339}
340
341#[fixture]
343pub async fn nethsm_with_keys(
344 #[future] provisioned_nethsm: TestResult<(NetHsm, Container<NetHsmImage>)>,
345) -> TestResult<(NetHsm, Container<NetHsmImage>)> {
346 let (nethsm, container) = provisioned_nethsm.await?;
347
348 println!("Adding users and keys to container...");
349 add_users_to_nethsm(&nethsm)?;
350 add_keys_to_nethsm(&nethsm)?;
351
352 Ok((nethsm, container))
353}
354
355#[fixture]
357pub fn update_file() -> TestResult<PathBuf> {
358 let file_name = "update.img.bin";
359 let update_link = format!(
360 "https://raw.githubusercontent.com/Nitrokey/nethsm-sdk-py/main/tests/{}",
361 file_name
362 );
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}