mod common;
use crate::common::init_sdk_with_config;
use common::{create_id_all_classes, create_second_user, init_sdk_get_user, initialize_sdk};
use galvanic_assert::{
matchers::{collection::contains_in_any_order, eq},
*,
};
use ironoxide::prelude::*;
use itertools::EitherOrBoth;
use std::convert::{TryFrom, TryInto};
#[tokio::test]
async fn doc_list() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let (other_user, _) = init_sdk_get_user().await;
let doc = "secret".as_bytes();
let opts =
DocumentEncryptOpts::with_explicit_grants(None, None, false, vec![(&other_user).into()]);
sdk.document_encrypt(doc, &opts).await?;
let document_list = sdk.document_list().await?;
dbg!(&document_list);
assert_eq!(document_list.result().len(), 1);
Ok(())
}
#[tokio::test]
async fn doc_roundtrip_empty_data() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 0];
let doc_result = sdk.document_encrypt(&doc, &Default::default()).await?;
let decrypted_result = sdk.document_decrypt(doc_result.encrypted_data()).await?;
Ok(assert_eq!(&doc, decrypted_result.decrypted_data()))
}
#[tokio::test]
async fn doc_create_without_id() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 64];
let doc_result = sdk.document_encrypt(&doc, &Default::default()).await?;
assert_eq!(doc_result.grants().len(), 1); assert_eq!(doc_result.access_errs().len(), 0);
Ok(())
}
#[tokio::test]
async fn doc_create_with_policy_grants() -> Result<(), IronOxideErr> {
let (curr_user, sdk) = init_sdk_get_user().await;
let data_rec_group_id: GroupId = format!("data_recovery_{}", curr_user.id()).try_into()?;
sdk.group_create(&GroupCreateOpts::new(
data_rec_group_id.clone().into(),
None,
true,
true,
None,
vec![],
vec![],
false,
))
.await?;
let doc = [0u8; 64];
let doc_result = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_policy_grants(
None,
Some("doc name".try_into()?),
PolicyGrant::new(
Some("PII".try_into()?),
Some("INTERNAL".try_into()?),
None,
None,
),
),
)
.await?;
assert_eq!(doc_result.grants().len(), 2);
assert_that!(
&doc_result
.grants()
.iter()
.map(Clone::clone)
.collect::<Vec<UserOrGroup>>(),
contains_in_any_order(vec![
UserOrGroup::User {
id: sdk.device().account_id().clone()
},
UserOrGroup::Group {
id: data_rec_group_id.clone()
}
])
);
assert_eq!(doc_result.access_errs().len(), 2);
assert_that!(
&doc_result
.access_errs()
.iter()
.map(|err| err.user_or_group.clone())
.collect::<Vec<_>>(),
contains_in_any_order(vec![
UserOrGroup::Group {
id: "badgroupid_frompolicy".try_into()?
},
UserOrGroup::User {
id: "baduserid_frompolicy".try_into()?
}
])
);
let user2_result = create_second_user().await;
let user2 = user2_result.account_id();
let group2_id: GroupId = format!("group_other_{}", user2.id()).try_into()?;
sdk.group_create(&GroupCreateOpts::new(
group2_id.clone().into(),
None,
true,
false,
None,
vec![],
vec![],
false,
))
.await?;
let doc_result2 = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_policy_grants(
None,
Some("doc name2".try_into()?),
PolicyGrant::new(
Some("HEALTH".try_into()?),
Some("RESTRICTED".try_into()?),
Some("PATIENT".try_into()?),
Some(user2.clone()),
),
),
)
.await?;
assert_eq!(doc_result2.grants().len(), 3);
assert_that!(
&doc_result2
.grants()
.iter()
.map(Clone::clone)
.collect::<Vec<UserOrGroup>>(),
contains_in_any_order(vec![
UserOrGroup::User { id: user2.clone() },
UserOrGroup::Group { id: group2_id },
UserOrGroup::Group {
id: data_rec_group_id.clone()
}
])
);
assert_eq!(doc_result2.access_errs().len(), 1);
assert_that!(
&doc_result2
.access_errs()
.iter()
.map(|err| err.user_or_group.clone())
.collect::<Vec<_>>(),
contains_in_any_order(vec![UserOrGroup::Group {
id: "group_id_doctors".try_into()?
},])
);
let doc_result3 = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_policy_grants(
None,
Some("doc name2".try_into()?),
PolicyGrant::default(),
),
)
.await?;
assert_eq!(doc_result3.grants().len(), 1);
assert_eq!(sdk.clear_policy_cache(), 1);
Ok(())
}
fn setup_encrypt_with_explicit_self_grant() -> DocumentEncryptOpts {
let bad_user: UserId = "bad_user".try_into().unwrap();
let bad_group: GroupId = "bad_group".try_into().unwrap();
DocumentEncryptOpts::with_explicit_grants(
None,
Some("first name".try_into().unwrap()),
true,
vec![
UserOrGroup::User { id: bad_user },
UserOrGroup::Group { id: bad_group },
],
)
}
fn check_encrypt_with_explicit_self_grant(sdk: &IronOxide, doc_result: Box<dyn WithGrantsAndErrs>) {
let bad_user: UserId = "bad_user".try_into().unwrap();
let bad_group: GroupId = "bad_group".try_into().unwrap();
assert_eq!(doc_result.grants().len(), 1);
assert_eq!(
doc_result.grants()[0],
UserOrGroup::User {
id: sdk.device().account_id().clone()
}
);
assert_eq!(doc_result.access_errs().len(), 2);
assert_that!(
&doc_result
.access_errs()
.iter()
.map(|err| err.user_or_group.clone())
.collect::<Vec<_>>(),
contains_in_any_order(vec![
UserOrGroup::User { id: bad_user },
UserOrGroup::Group { id: bad_group }
])
)
}
#[tokio::test]
async fn doc_create_with_explicit_self_grant() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let encrypt_opts = setup_encrypt_with_explicit_self_grant();
let doc = [0u8; 64];
let doc_result = sdk.document_encrypt(&doc, &encrypt_opts).await?;
check_encrypt_with_explicit_self_grant(&sdk, Box::new(doc_result));
Ok(())
}
#[tokio::test]
async fn doc_encrypt_unmanaged_with_explicit_self_grant() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let encrypt_opts = setup_encrypt_with_explicit_self_grant();
let doc = [0u8; 64];
let doc_result = sdk.document_encrypt_unmanaged(&doc, &encrypt_opts).await?;
check_encrypt_with_explicit_self_grant(&sdk, Box::new(doc_result));
Ok(())
}
fn check_encrypt_with_explicit_and_policy_grants(
curr_user: &UserId,
ex_group_id: &GroupId,
data_rec_group_id: &GroupId,
bad_group_id: &GroupId,
doc_result: Box<dyn WithGrantsAndErrs>,
) {
assert_eq!(doc_result.grants().len(), 3);
assert_that!(
&doc_result
.grants()
.iter()
.map(Clone::clone)
.collect::<Vec<UserOrGroup>>(),
contains_in_any_order(vec![
UserOrGroup::User {
id: curr_user.clone()
},
UserOrGroup::Group {
id: data_rec_group_id.clone()
},
UserOrGroup::Group {
id: ex_group_id.clone()
}
])
);
assert_eq!(doc_result.access_errs().len(), 3);
assert_that!(
&doc_result
.access_errs()
.iter()
.map(|err| err.user_or_group.clone())
.collect::<Vec<_>>(),
contains_in_any_order(vec![
UserOrGroup::Group {
id: "badgroupid_frompolicy".try_into().unwrap()
},
UserOrGroup::User {
id: "baduserid_frompolicy".try_into().unwrap()
},
UserOrGroup::Group {
id: bad_group_id.clone()
} ])
);
}
async fn setup_encrypt_with_explicit_and_policy_grants(
sdk: &IronOxide,
curr_user: &UserId,
bad_group: &GroupId,
) -> Result<(DocumentEncryptOpts, GroupId, GroupId), IronOxideErr> {
let data_rec_group_id: GroupId = format!("data_recovery_{}", curr_user.id()).try_into()?;
sdk.group_create(&GroupCreateOpts::new(
data_rec_group_id.clone().into(),
None,
true,
true,
None,
vec![],
vec![],
false,
))
.await?;
let group2 = sdk.group_create(&Default::default()).await?;
let ex_group_id = group2.id();
Ok((
DocumentEncryptOpts::new(
None,
None,
EitherOrBoth::Both(
ExplicitGrant::new(true, &[ex_group_id.into(), bad_group.into()]),
PolicyGrant::new(
Some("PII".try_into()?),
Some("INTERNAL".try_into()?),
None,
None,
),
),
),
ex_group_id.clone(),
data_rec_group_id.clone(),
))
}
#[tokio::test]
async fn doc_create_with_explicit_and_policy_grants() -> Result<(), IronOxideErr> {
let (curr_user, sdk) = init_sdk_get_user().await;
let bad_group: GroupId = create_id_all_classes("bad_group").try_into()?;
let doc = [0u8; 64];
let (opts, ex_group_id, data_rec_group_id) =
setup_encrypt_with_explicit_and_policy_grants(&sdk, &curr_user, &bad_group).await?;
let doc_result = sdk.document_encrypt(&doc, &opts).await?;
check_encrypt_with_explicit_and_policy_grants(
&curr_user,
&ex_group_id,
&data_rec_group_id,
&bad_group,
Box::new(doc_result),
);
Ok(())
}
#[tokio::test]
async fn doc_encrypt_unmanaged_with_explicit_and_policy_grants() -> Result<(), IronOxideErr> {
let (curr_user, sdk) = init_sdk_get_user().await;
let bad_group: GroupId = create_id_all_classes("bad_group").try_into()?;
let doc = [0u8; 64];
let (opts, ex_group_id, data_rec_group_id) =
setup_encrypt_with_explicit_and_policy_grants(&sdk, &curr_user, &bad_group).await?;
let doc_result = sdk.document_encrypt_unmanaged(&doc, &opts).await?;
check_encrypt_with_explicit_and_policy_grants(
&curr_user,
&ex_group_id,
&data_rec_group_id,
&bad_group,
Box::new(doc_result),
);
Ok(())
}
#[tokio::test]
async fn doc_create_duplicate_grants() -> Result<(), IronOxideErr> {
let (user, sdk) = init_sdk_get_user().await;
let doc = [0u8; 64];
let doc_result = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_explicit_grants(
None,
Some("first name".try_into()?),
true,
vec![UserOrGroup::User { id: user }],
),
)
.await?;
assert_that!(&doc_result.grants().len(), eq(1));
Ok(())
}
#[tokio::test]
async fn doc_create_without_self_grant() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 64];
let second_user = create_second_user().await;
let doc_result = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_explicit_grants(
Some(create_id_all_classes("").try_into()?),
Some("first name".try_into()?),
false,
vec![UserOrGroup::User {
id: second_user.account_id().clone(),
}],
),
)
.await?;
assert_eq!(doc_result.grants().len(), 1);
assert_ne!(
doc_result.grants()[0],
UserOrGroup::User {
id: sdk.device().account_id().clone()
}
);
assert_eq!(
doc_result.grants()[0],
UserOrGroup::User {
id: second_user.account_id().clone()
}
);
assert_eq!(doc_result.access_errs().len(), 0);
Ok(())
}
#[tokio::test]
async fn doc_create_must_grant() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 64];
let doc_result = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_explicit_grants(
None,
Some("first name".try_into()?),
false,
vec![],
),
)
.await;
assert_eq!(
match doc_result.err().unwrap() {
IronOxideErr::ValidationError(field_name, _) => field_name,
_ => "failed test".to_string(),
},
"grants".to_string()
);
Ok(())
}
#[tokio::test]
async fn doc_create_and_adjust_name() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 64];
let doc_result = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_explicit_grants(
Some(create_id_all_classes("").try_into()?),
Some("first name".try_into()?),
true,
vec![UserOrGroup::User {
id: UserId::try_from("bad-user").expect("should be good id"),
}],
),
)
.await?;
assert_eq!(doc_result.name().unwrap().name(), &"first name".to_string());
let first_update = sdk
.document_update_name(&doc_result.id(), Some(&"second name".try_into()?))
.await?;
assert_eq!(
first_update.name().unwrap().name(),
&"second name".to_string()
);
let last_update = sdk.document_update_name(&doc_result.id(), None).await?;
assert!(last_update.name().is_none());
Ok(())
}
#[tokio::test]
async fn doc_encrypt_decrypt_roundtrip() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [43u8; 64];
let encrypted_doc = sdk.document_encrypt(&doc, &Default::default()).await?;
sdk.document_get_metadata(&encrypted_doc.id()).await?;
let decrypted = sdk
.document_decrypt(&encrypted_doc.encrypted_data())
.await?;
assert_eq!(doc.to_vec(), decrypted.decrypted_data());
Ok(())
}
#[tokio::test]
async fn doc_decrypt_unmanaged_no_access() -> Result<(), IronOxideErr> {
use std::borrow::Borrow;
let sdk = initialize_sdk().await?;
let user2 = create_second_user().await;
let doc = [43u8; 64];
let encrypted_doc = sdk
.document_encrypt_unmanaged(
&doc,
&DocumentEncryptOpts::with_explicit_grants(
Some(create_id_all_classes("").try_into()?),
None,
false,
vec![user2.account_id().borrow().into()],
),
)
.await?;
let decrypt_err = sdk
.document_decrypt_unmanaged(
&encrypted_doc.encrypted_data(),
&encrypted_doc.encrypted_deks(),
)
.await
.unwrap_err();
assert_that!(&decrypt_err, is_variant!(IronOxideErr::RequestServerErrors));
Ok(())
}
#[tokio::test]
async fn decrypt_with_rotated_user_private_key() -> Result<(), IronOxideErr> {
let (_, init_result) = common::init_sdk_get_init_result(true).await;
let sdk = init_result.discard_check();
let encrypted_doc = sdk
.document_encrypt(
&[42u8, 43u8],
&DocumentEncryptOpts::with_explicit_grants(None, None, true, vec![]),
)
.await?;
let decrypt_result1 = sdk.document_decrypt(encrypted_doc.encrypted_data()).await?;
sdk.user_rotate_private_key(common::USER_PASSWORD).await?;
let decrypt_result2 = sdk.document_decrypt(encrypted_doc.encrypted_data()).await?;
assert_eq!(&decrypt_result1, &decrypt_result2);
Ok(())
}
#[tokio::test]
async fn doc_encrypt_decrypt_unmanaged_roundtrip() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let encrypt_opts = Default::default();
let doc = [0u8; 42];
let encrypt_result = sdk.document_encrypt_unmanaged(&doc, &encrypt_opts).await?;
let decrypt_result = sdk
.document_decrypt_unmanaged(
&encrypt_result.encrypted_data(),
&encrypt_result.encrypted_deks(),
)
.await?;
assert_eq!(&doc[..], decrypt_result.decrypted_data());
Ok(())
}
#[tokio::test]
async fn doc_encrypt_update_and_decrypt() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc1 = [20u8; 72];
let encrypted_doc = sdk.document_encrypt(&doc1, &Default::default()).await?;
let doc_id = &encrypted_doc.id();
let doc2 = [10u8; 11];
let updated_encrypted_doc = sdk.document_update_bytes(doc_id, &doc2).await?;
let decrypted = sdk
.document_decrypt(&updated_encrypted_doc.encrypted_data())
.await?;
assert_eq!(doc2.to_vec(), decrypted.decrypted_data());
Ok(())
}
#[tokio::test]
async fn doc_grant_access() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 64];
let doc_result = sdk.document_encrypt(&doc, &Default::default()).await?;
let doc_id = doc_result.id().clone();
let user = create_second_user().await;
let group_result = sdk.group_create(&Default::default()).await?;
let group_id = group_result.id().clone();
let group2_result = sdk
.group_create(&GroupCreateOpts::new(
None,
None,
true,
false,
None,
vec![],
vec![],
false,
))
.await?;
let group2_id = group2_result.id().clone();
let grants = sdk
.document_grant_access(
&doc_id,
&vec![
UserOrGroup::User {
id: user.account_id().clone(),
},
UserOrGroup::Group { id: group_id },
UserOrGroup::Group { id: group2_id },
UserOrGroup::User {
id: create_id_all_classes("bad-user-id").try_into()?,
},
UserOrGroup::Group {
id: create_id_all_classes("bad-group-id").try_into()?,
},
],
)
.await?;
assert_eq!(3, grants.succeeded().len());
assert_eq!(2, grants.failed().len());
Ok(())
}
#[tokio::test]
async fn doc_revoke_access() -> Result<(), IronOxideErr> {
let sdk = initialize_sdk().await?;
let doc = [0u8; 64];
let doc_result = sdk
.document_encrypt(
&doc,
&DocumentEncryptOpts::with_explicit_grants(
Some(create_id_all_classes("").try_into()?),
None,
true,
vec![],
),
)
.await?;
let doc_id = doc_result.id().clone();
let user = create_second_user().await;
let group_result = sdk.group_create(&Default::default()).await?;
let group_id = group_result.id().clone();
let grants = sdk
.document_grant_access(
&doc_id,
&vec![
UserOrGroup::User {
id: user.account_id().clone(),
},
UserOrGroup::Group {
id: group_id.clone(),
},
],
)
.await?;
assert_eq!(grants.succeeded().len(), 2);
let revokes = sdk
.document_revoke_access(
&doc_id,
&vec![
UserOrGroup::User {
id: user.account_id().clone(),
},
UserOrGroup::Group {
id: group_id.clone(),
},
UserOrGroup::User {
id: "bad-user-id".try_into()?,
},
UserOrGroup::Group {
id: "bad-group-id".try_into()?,
},
],
)
.await?;
assert_eq!(revokes.succeeded().len(), 2);
assert_eq!(revokes.failed().len(), 2);
Ok(())
}
#[tokio::test]
async fn sdk_init_with_timeout() -> Result<(), IronOxideErr> {
let result = init_sdk_with_config(&IronOxideConfig {
sdk_operation_timeout: Some(std::time::Duration::from_millis(10)),
..Default::default()
})
.await;
assert!(result.is_err());
let err_result = result.unwrap_err();
assert_that!(&err_result, is_variant!(IronOxideErr::OperationTimedOut));
assert_that!(
&err_result,
has_structure!(IronOxideErr::OperationTimedOut {
operation: eq(SdkOperation::InitializeSdk),
duration: eq(std::time::Duration::from_millis(10))
})
);
Ok(())
}
trait WithGrantsAndErrs {
fn grants(&self) -> Vec<UserOrGroup>;
fn access_errs(&self) -> &[DocAccessEditErr];
}
impl WithGrantsAndErrs for DocumentEncryptResult {
fn grants(&self) -> Vec<UserOrGroup> {
self.grants().to_vec()
}
fn access_errs(&self) -> &[DocAccessEditErr] {
self.access_errs()
}
}
impl WithGrantsAndErrs for DocumentEncryptUnmanagedResult {
fn grants(&self) -> Vec<UserOrGroup> {
self.grants().to_vec()
}
fn access_errs(&self) -> &[DocAccessEditErr] {
self.access_errs()
}
}