use cosmian_kms_client::{
KmsClient,
kmip_2_1::{KmipOperation, kmip_types::UniqueIdentifier},
reexport::cosmian_kms_client_utils::symmetric_utils::DataEncryptionAlgorithm,
};
use cosmian_logger::{log_init, trace};
use serial_test::serial;
use tempfile::TempDir;
#[cfg(not(feature = "non-fips"))]
use test_kms_server::start_default_test_kms_server_with_privileged_users;
use test_kms_server::{init_test_logging, start_default_test_kms_server_with_cert_auth};
#[cfg(not(feature = "non-fips"))]
use crate::actions::kms::elliptic_curves::keys::create_key_pair::CreateKeyPairAction;
#[cfg(not(feature = "non-fips"))]
use crate::actions::kms::shared::ImportSecretDataOrKeyAction;
use crate::{
actions::kms::{
access::{
GrantAccess, ListAccessRightsObtained, ListAccessesGranted, ListOwnedObjects,
RevokeAccess,
},
shared::ExportSecretDataOrKeyAction,
symmetric::keys::{
create_key::CreateKeyAction, destroy_key::DestroyKeyAction, revoke_key::RevokeKeyAction,
},
},
error::result::KmsCliResult,
tests::kms::symmetric::encrypt_decrypt::run_encrypt_decrypt_test,
};
async fn gen_key(kms_client: &KmsClient) -> KmsCliResult<UniqueIdentifier> {
CreateKeyAction::default().run(kms_client.clone()).await
}
#[cfg(not(feature = "non-fips"))]
async fn gen_keypair(kms_client: &KmsClient) -> KmsCliResult<(UniqueIdentifier, UniqueIdentifier)> {
CreateKeyPairAction::default().run(kms_client.clone()).await
}
#[cfg(not(feature = "non-fips"))]
async fn export_import_sym_key(key_id: &str, kms_client: &KmsClient) -> KmsCliResult<String> {
let tmp_dir = TempDir::new()?;
let export_path = tmp_dir.path().join("output.export");
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_owned()),
key_file: export_path.clone(),
..Default::default()
}
.run(kms_client.clone())
.await?;
Ok(ImportSecretDataOrKeyAction {
key_file: export_path,
..Default::default()
}
.run(kms_client.clone())
.await?
.to_string())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_ownership_and_grant() -> KmsCliResult<()> {
let tmp_dir = TempDir::new()?;
let output_json = tmp_dir.path().join("output.json");
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
ExportSecretDataOrKeyAction {
key_file: output_json.clone(),
key_id: Some(key_id.to_string()),
..Default::default()
}
.run(ctx.get_owner_client())
.await?;
run_encrypt_decrypt_test(
&ctx.get_owner_client(),
&key_id,
DataEncryptionAlgorithm::AesGcm,
None,
0,
)
.await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json.clone(),
..Default::default()
}
.run(ctx.get_user_client())
.await
.unwrap_err();
run_encrypt_decrypt_test(
&ctx.get_user_client(),
&key_id,
DataEncryptionAlgorithm::AesGcm,
None,
0,
)
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "failed revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Decrypt, KmipOperation::Encrypt],
}
.run(ctx.get_owner_client())
.await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json.clone(),
..Default::default()
}
.run(ctx.get_user_client())
.await
.unwrap_err();
run_encrypt_decrypt_test(
&ctx.get_user_client(),
&key_id,
DataEncryptionAlgorithm::AesGcm,
None,
0,
)
.await?;
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "failed revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json.clone(),
..Default::default()
}
.run(ctx.get_user_client())
.await?;
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "failed revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Revoke],
}
.run(ctx.get_owner_client())
.await?;
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "user revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Destroy],
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await?;
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_grant_error() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
GrantAccess {
object_uid: Some("BAD ID".to_owned()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "owner.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_revoke_access() -> KmsCliResult<()> {
init_test_logging();
let tmp_dir = TempDir::new()?;
let output_json = tmp_dir.path().join("output.json");
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
ExportSecretDataOrKeyAction {
key_file: output_json.clone(),
key_id: Some(key_id.to_string()),
..Default::default()
}
.run(ctx.get_user_client())
.await?;
RevokeAccess {
user: "user.client@acme.com".to_owned(),
object_uid: Some(key_id.to_string()),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
ExportSecretDataOrKeyAction {
key_file: output_json,
key_id: Some(key_id.to_string()),
..Default::default()
}
.run(ctx.get_user_client())
.await
.unwrap_err();
RevokeAccess {
object_uid: Some("BAD KEY".to_owned()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeAccess {
object_uid: Some(key_id.to_string()),
user: "BAD USER".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_user_client())
.await
.unwrap_err();
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_list_access_rights() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
let owner_list = ListAccessesGranted {
object_uid: key_id.to_string(),
}
.run(ctx.get_owner_client())
.await?;
trace!("owner list count {}", owner_list.len());
assert!(
owner_list
.iter()
.map(|x| x.user_id.clone())
.any(|x| x == *"user.client@acme.com")
);
ListAccessesGranted {
object_uid: key_id.to_string(),
}
.run(ctx.get_user_client())
.await
.unwrap_err();
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_list_access_rights_error() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server_with_cert_auth().await;
ListAccessesGranted {
object_uid: "BAD KEY".to_owned(),
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_list_owned_objects() -> KmsCliResult<()> {
init_test_logging();
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
let user_list = ListOwnedObjects.run(ctx.get_user_client()).await?;
assert!(
user_list
.iter()
.map(|x| x.object_id.clone())
.all(|x| x != key_id)
);
let owner_list = ListOwnedObjects.run(ctx.get_owner_client()).await?;
assert!(
owner_list
.iter()
.map(|x| x.object_id.clone())
.any(|x| x == key_id)
);
let user_key_id = gen_key(&ctx.get_user_client()).await?;
let user_list = ListOwnedObjects.run(ctx.get_user_client()).await?;
assert!(
user_list
.iter()
.map(|x| x.object_id.clone())
.any(|x| x == user_key_id)
);
let owner_list = ListOwnedObjects.run(ctx.get_owner_client()).await?;
assert!(
!owner_list
.iter()
.map(|x| x.object_id.clone())
.any(|x| x == user_key_id)
);
assert!(
owner_list
.iter()
.map(|x| x.object_id.clone())
.any(|x| x == key_id)
);
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_access_right_obtained() -> KmsCliResult<()> {
init_test_logging();
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
let list = ListAccessRightsObtained.run(ctx.get_owner_client()).await?;
trace!("owner list count {}", list.len());
assert!(
list.iter()
.map(|x| x.object_id.clone())
.all(|x| x != key_id)
);
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
let list = ListAccessRightsObtained.run(ctx.get_user_client()).await?;
trace!("user list count {}", list.len());
assert!(
list.iter()
.map(|x| x.object_id.clone())
.any(|x| x == key_id)
);
assert!(
list.iter()
.flat_map(|x| x.operations.clone())
.any(|x| x == KmipOperation::Get)
);
let list = ListAccessRightsObtained.run(ctx.get_owner_client()).await?;
assert!(
!list
.iter()
.map(|x| x.object_id.clone())
.any(|x| x == key_id)
);
let key_id = gen_key(&ctx.get_owner_client()).await?;
let list = ListAccessRightsObtained.run(ctx.get_owner_client()).await?;
assert!(!list.iter().any(|x| x.object_id == key_id));
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "*".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Encrypt],
}
.run(ctx.get_owner_client())
.await?;
let list = ListAccessRightsObtained.run(ctx.get_user_client()).await?;
trace!("user list count {}", list.len());
assert!(list.iter().any(|x| x.object_id == key_id));
assert!(
list.iter()
.flat_map(|x| x.operations.clone())
.any(|x| x == KmipOperation::Get)
);
assert!(
list.iter()
.flat_map(|x| x.operations.clone())
.any(|x| x == KmipOperation::Encrypt)
);
let list = ListAccessRightsObtained.run(ctx.get_owner_client()).await?;
assert!(!list.iter().any(|x| x.object_id == key_id));
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_ownership_and_grant_wildcard_user() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server_with_cert_auth().await;
let tmp_dir = TempDir::new()?;
let output_json = tmp_dir.path().join("output.json");
let key_id = gen_key(&ctx.get_owner_client()).await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json.clone(),
..Default::default()
}
.run(ctx.get_owner_client())
.await?;
run_encrypt_decrypt_test(
&ctx.get_owner_client(),
&key_id,
DataEncryptionAlgorithm::AesGcm,
None,
0,
)
.await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json.clone(),
..Default::default()
}
.run(ctx.get_user_client())
.await
.unwrap_err();
run_encrypt_decrypt_test(
&ctx.get_user_client(),
&key_id,
DataEncryptionAlgorithm::AesGcm,
None,
0,
)
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "failed revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Encrypt, KmipOperation::Decrypt],
}
.run(ctx.get_owner_client())
.await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json.clone(),
..Default::default()
}
.run(ctx.get_user_client())
.await
.unwrap_err();
run_encrypt_decrypt_test(
&ctx.get_user_client(),
&key_id,
DataEncryptionAlgorithm::AesGcm,
None,
0,
)
.await?;
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "failed revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await?;
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_string()),
key_file: output_json,
..Default::default()
}
.run(ctx.get_user_client())
.await?;
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "failed revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Revoke],
}
.run(ctx.get_owner_client())
.await?;
RevokeKeyAction {
key_id: Some(key_id.to_string()),
revocation_reason: "user revoke".to_owned(),
tags: None,
}
.run(ctx.get_user_client())
.await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Destroy],
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_user_client())
.await?;
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_grant_multiple_operations() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server_with_cert_auth().await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![
KmipOperation::Get,
KmipOperation::Revoke,
KmipOperation::Encrypt,
],
}
.run(ctx.get_owner_client())
.await?;
let owner_list = ListAccessesGranted {
object_uid: key_id.to_string(),
}
.run(ctx.get_owner_client())
.await?;
assert!(
owner_list
.iter()
.any(|x| x.user_id == "user.client@acme.com")
);
RevokeAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get, KmipOperation::Revoke],
}
.run(ctx.get_owner_client())
.await?;
let owner_list = ListAccessesGranted {
object_uid: key_id.to_string(),
}
.run(ctx.get_owner_client())
.await?;
let user_rights = owner_list
.iter()
.find(|x| x.user_id == "user.client@acme.com")
.unwrap();
assert_eq!(user_rights.operations.len(), 1);
assert!(user_rights.operations.contains(&KmipOperation::Encrypt));
Ok(())
}
#[tokio::test]
#[serial]
pub(crate) async fn test_grant_with_without_object_uid() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server_with_cert_auth().await;
GrantAccess {
object_uid: None,
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Create],
}
.run(ctx.get_owner_client())
.await?;
GrantAccess {
object_uid: None,
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Create, KmipOperation::Get],
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
Ok(())
}
#[cfg(not(feature = "non-fips"))]
#[tokio::test]
#[serial]
pub(crate) async fn test_privileged_users() -> KmsCliResult<()> {
init_test_logging();
let ctx = start_default_test_kms_server_with_privileged_users(vec![
"owner.client@acme.com".to_owned(),
"user.privileged@acme.com".to_owned(),
])
.await;
let key_id = gen_key(&ctx.get_owner_client()).await?;
GrantAccess {
object_uid: Some(key_id.to_string()),
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Get, KmipOperation::Export],
}
.run(ctx.get_owner_client())
.await?;
let (_pub_key, _priv_key) = gen_keypair(&ctx.get_owner_client()).await?;
let _imported_key = export_import_sym_key(&key_id.to_string(), &ctx.get_owner_client()).await?;
gen_key(&ctx.get_user_client()).await.unwrap_err();
gen_keypair(&ctx.get_user_client()).await.unwrap_err();
export_import_sym_key(&key_id.to_string(), &ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: None,
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Create],
}
.run(ctx.get_owner_client())
.await?;
gen_key(&ctx.get_user_client()).await.unwrap();
gen_keypair(&ctx.get_user_client()).await.unwrap();
export_import_sym_key(&key_id.to_string(), &ctx.get_user_client())
.await
.unwrap();
GrantAccess {
object_uid: None,
user: "user2.client@acme.com".to_owned(),
operations: vec![KmipOperation::Create],
}
.run(ctx.get_user_client())
.await
.unwrap_err();
GrantAccess {
object_uid: None,
user: "user.privileged@acme.com".to_owned(),
operations: vec![KmipOperation::Create],
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeAccess {
object_uid: None,
user: "user.client@acme.com".to_owned(),
operations: vec![KmipOperation::Create],
}
.run(ctx.get_owner_client())
.await?;
gen_key(&ctx.get_user_client()).await.unwrap_err();
gen_keypair(&ctx.get_user_client()).await.unwrap_err();
export_import_sym_key(&key_id.to_string(), &ctx.get_user_client())
.await
.unwrap_err();
RevokeAccess {
object_uid: None,
user: "user.privileged@acme.com".to_owned(),
operations: vec![KmipOperation::Create],
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
Ok(())
}