Skip to main content

nethsm/
test.rs

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;
9// Publicly re-export, so that consumers do not have to rely on rustainers directly .
10pub 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
27/// Identifier for an admin user.
28pub static ADMIN_USER_ID: &str = "admin";
29
30/// Sample admin passphrase.
31pub static ADMIN_USER_PASSPHRASE: &str = "just-an-admin-passphrase";
32
33/// Sample unlock passphrase.
34pub static UNLOCK_PASSPHRASE: &str = "just-an-unlock-passphrase";
35
36/// Default user ID for an operator.
37pub static DEFAULT_OPERATOR_USER_ID: &str = "operator1";
38
39/// Default real name for an operator.
40pub static DEFAULT_OPERATOR_USER_REAL_NAME: &str = "Some Operator";
41
42/// Sample operator passphrase.
43pub static DEFAULT_OPERATOR_USER_PASSPHRASE: &str = "just-an-operator-passphrase";
44
45/// User ID for a different user.
46pub static OTHER_OPERATOR_USER_ID: &str = "operator2";
47
48/// Real name for a different user.
49pub static OTHER_OPERATOR_USER_REAL_NAME: &str = "Some Other Operator";
50
51/// Sample passphrase for a different user.
52pub static OTHER_OPERATOR_USER_PASSPHRASE: &str = "just-another-operator-passphrase";
53
54/// User ID for backup purposes.
55pub static BACKUP_USER_ID: &str = "backup1";
56
57/// Real name for the backup user.
58pub static BACKUP_USER_REAL_NAME: &str = "Some Backup";
59
60/// Sample passphrase for the backup user.
61pub static BACKUP_USER_PASSPHRASE: &str = "just-a-backup-passphrase";
62
63/// User ID for the metrics user.
64pub static METRICS_USER_ID: &str = "metrics1";
65
66/// Real name for the metrics user.
67pub static METRICS_USER_REAL_NAME: &str = "Some Metrics";
68
69/// Sample passphrase for the metrics user.
70pub static METRICS_USER_PASSPHRASE: &str = "just-a-metrics-passphrase";
71
72/// Default size of the RSA key in bits.
73pub static DEFAULT_RSA_BITS: u32 = 2048;
74
75/// Default ID for a key.
76pub static DEFAULT_KEY_ID: &str = "key1";
77
78/// Default ID for a different key.
79pub static OTHER_KEY_ID: &str = "key2";
80
81/// Default tag.
82pub static DEFAULT_TAG: &str = "tag1";
83
84/// Different tag.
85pub static OTHER_TAG: &str = "tag2";
86
87/// Default ID for the encryption key.
88pub static ENC_KEY_ID: &str = "enckey1";
89
90/// Default tag for the encryption key.
91pub static ENC_TAG: &str = "enctag1";
92
93/// User ID for the operator user who can access the encryption key.
94pub static ENC_OPERATOR_USER_ID: &str = "encoperator1";
95
96/// Real name for the operator user who can access the encryption key.
97pub static ENC_OPERATOR_USER_REAL_NAME: &str = "Some Encryption Operator";
98
99/// Sample passphrase for the operator user who can access the encryption key.
100pub static ENC_OPERATOR_USER_PASSPHRASE: &str = "just-an-encryption-passphrase";
101
102/// Default size for the AES key in bits.
103pub static DEFAULT_AES_BITS: u32 = 128;
104
105/// Sample namespace.
106pub static NAMESPACE1: &str = "namespace1";
107
108/// Administrator's user ID for `namespace1`.
109pub static NAMESPACE1_ADMIN_USER_ID: &str = "namespace1~admin";
110
111/// Sample passphrase for `namespace1`'s administrator.
112pub static NAMESPACE1_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace-admin-passphrase";
113
114/// Real name for `namespace1`'s administrator.
115pub static NAMESPACE1_ADMIN_REAL_NAME: &str = "Namespace1 Admin";
116
117/// User ID of an operator in `namespace1`.
118pub static NAMESPACE1_OPERATOR_USER_ID: &str = "namespace1~operator";
119
120/// Sample passphrase of an operator in `namespace1`.
121pub static NAMESPACE1_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace-operator-passphrase";
122
123/// Real name of an operator in `namespace1`.
124pub static NAMESPACE1_OPERATOR_REAL_NAME: &str = "Namespace1 Operator";
125
126/// Second namespace.
127pub static NAMESPACE2: &str = "namespace2";
128
129/// Administrator's user ID for `namespace2`.
130pub static NAMESPACE2_ADMIN_USER_ID: &str = "namespace2~admin";
131
132/// Sample passphrase for `namespace2`'s administrator.
133pub static NAMESPACE2_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace2-admin-passphrase";
134
135/// Real name for `namespace2`'s administrator.
136pub static NAMESPACE2_ADMIN_REAL_NAME: &str = "Namespace2 Admin";
137
138/// User ID of an operator in `namespace2`.
139pub static NAMESPACE2_OPERATOR_USER_ID: &str = "namespace2~operator";
140
141/// Sample passphrase of an operator in `namespace2`.
142pub static NAMESPACE2_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace2-operator-passphrase";
143
144/// Real name of an operator in `namespace2`.
145pub static NAMESPACE2_OPERATOR_REAL_NAME: &str = "Namespace2 Operator";
146
147mod container;
148pub use container::NetHsmImage;
149
150/// Creates and starts a new NetHSM container.
151pub 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
161/// Creates a new [NetHsm] object configured with administrator credentials.
162pub 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/// Returns a new [NetHsm] object pointing to an unprovisioned NetHSM.
175#[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        // skip symmetric keys, as for those we do not have a public key
317        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/// Creates a new [NetHsm] object pointing at a provisioned NetHSM container.
328#[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/// Creates a new [NetHsm] object pointing at a NetHSM container with users.
339#[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/// Adds users and keys to an already provisioned NetHSM container.
352#[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/// Downloads an update file if it's not already present.
366#[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}