use super::utils::{corrupt_container, create_containers_req};
use crate::{
app_auth::{app_state, AppState},
client::AuthClient,
config::{self, get_app_revocation_queue, push_to_app_revocation_queue},
errors::AuthError,
revocation,
test_utils::{
access_container, create_account_and_login, create_authenticator, create_file, fetch_file,
get_container_from_authenticator_entry, rand_app, register_app, register_rand_app, revoke,
try_access_container, try_revoke,
},
{access_container, run, AuthFuture, Authenticator},
};
use futures::{future, Future};
use safe_core::{
app_container_name,
client::AuthActions,
ipc::req::container_perms_into_permission_set,
ipc::resp::AccessContainerEntry,
ipc::{AuthReq, Permission},
Client, CoreError, FutureExt, MDataInfo,
};
use safe_nd::{Error as SndError, MDataAddress, MDataSeqEntryActions, PublicKey};
use std::collections::HashMap;
use tiny_keccak::sha3_256;
fn verify_app_is_revoked(
client: &AuthClient,
app_id: String,
prev_ac_entry: AccessContainerEntry,
) -> Box<AuthFuture<()>> {
let c0 = client.clone();
let c1 = client.clone();
config::list_apps(client)
.and_then(move |(_, apps)| {
let auth_keys = c0.list_auth_keys_and_version().map_err(AuthError::from);
let state = app_state(&c0, &apps, &app_id);
let app_hash = sha3_256(app_id.as_bytes());
let app_key = PublicKey::from(unwrap!(apps.get(&app_hash)).keys.bls_pk);
auth_keys
.join(state)
.map(move |((auth_keys, _), state)| (auth_keys, app_key, state))
})
.and_then(move |(auth_keys, app_key, state)| -> Result<_, AuthError> {
if auth_keys.contains_key(&app_key) {
return Err(AuthError::Unexpected("App is still authenticated".into()));
}
assert_match!(state, AppState::Revoked);
Ok(app_key)
})
.and_then(move |app_key| {
let futures = prev_ac_entry.into_iter().map(move |(_, (mdata_info, _))| {
c1.list_mdata_user_permissions(*mdata_info.address(), app_key)
.then(|res| {
assert_match!(res, Err(CoreError::DataError(SndError::NoSuchKey)));
Ok(())
})
});
future::join_all(futures).map(|_| ())
})
.into_box()
}
fn verify_app_is_authenticated(client: &AuthClient, app_id: String) -> Box<AuthFuture<()>> {
let c0 = client.clone();
let c1 = client.clone();
let c2 = client.clone();
config::list_apps(client)
.then(move |res| {
let (_, mut apps) = unwrap!(res);
let app_hash = sha3_256(app_id.as_bytes());
let app_keys = unwrap!(apps.remove(&app_hash)).keys;
c0.list_auth_keys_and_version()
.map_err(AuthError::from)
.map(move |(auth_keys, _)| (auth_keys, app_id, app_keys))
})
.then(move |res| {
let (auth_keys, app_id, app_keys) = unwrap!(res);
let app_key = PublicKey::from(app_keys.bls_pk);
assert!(auth_keys.contains_key(&app_key));
access_container::fetch_entry(&c1, &app_id, app_keys)
.map(move |(_, entry)| (app_key, entry))
})
.then(move |res| {
let (app_key, ac_entry) = unwrap!(res);
let user = app_key;
let ac_entry = unwrap!(ac_entry);
let futures = ac_entry
.into_iter()
.map(move |(_, (mdata_info, permissions))| {
let expected_perms = container_perms_into_permission_set(&permissions);
let perms = c2
.list_mdata_user_permissions(*mdata_info.address(), user)
.then(move |res| {
let perms = unwrap!(res);
assert_eq!(perms, expected_perms);
Ok(())
});
let entries = c2
.list_seq_mdata_entries(mdata_info.name(), mdata_info.type_tag())
.then(move |res| {
let entries = unwrap!(res);
for (key, value) in entries {
if value.data.is_empty() {
continue;
}
let _ = unwrap!(mdata_info.decrypt(&key));
let _ = unwrap!(mdata_info.decrypt(&value.data));
}
Ok(())
});
perms.join(entries).map(|_| ())
});
future::join_all(futures).map(|_| ())
})
.into_box()
}
#[cfg(feature = "mock-network")]
mod mock_routing {
use super::*;
use crate::{
ffi::ipc::auth_flush_app_revocation_queue,
test_utils::{get_container_from_authenticator_entry, register_rand_app, try_revoke},
};
use config;
use ffi_utils::test_utils::call_0;
use maidsafe_utilities::SeededRng;
use safe_core::client::AuthActions;
use safe_core::ipc::{IpcError, Permission};
use safe_core::utils::test_utils::Synchronizer;
use safe_core::ConnectionManager;
use safe_nd::{Request, Response};
use std::{
collections::HashMap,
iter,
sync::{Arc, Barrier},
thread,
};
#[test]
fn app_revocation() {
let (auth, locator, password) = create_authenticator();
let auth_req = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let app_id = auth_req.app.id.clone();
let auth_granted = unwrap!(register_app(&auth, &auth_req));
let mut ac_entries = access_container(&auth, app_id.clone(), auth_granted.clone());
let (videos_md, _) = unwrap!(ac_entries.remove("_videos"));
let (docs_md, _) = unwrap!(ac_entries.remove("_documents"));
unwrap!(create_file(
&auth,
videos_md.clone(),
"video.mp4",
vec![1; 10],
true,
));
unwrap!(create_file(
&auth,
docs_md.clone(),
"test.doc",
vec![2; 10],
true
));
let auth = unwrap!(Authenticator::login(
locator.clone(),
password.clone(),
|| (),
));
unwrap!(try_revoke(&auth, &app_id));
let _ = unwrap!(fetch_file(&auth, docs_md.clone(), "test.doc"));
let new_videos_md = unwrap!(get_container_from_authenticator_entry(&auth, "_videos"));
let _ = unwrap!(fetch_file(&auth, new_videos_md, "video.mp4"));
let _ = unwrap!(fetch_file(&auth, videos_md.clone(), "video.mp4"));
let auth_keys = unwrap!(run(&auth, move |client| {
client
.list_auth_keys_and_version()
.map(move |(auth_keys, _version)| auth_keys)
.map_err(AuthError::from)
}));
assert!(!auth_keys.contains_key(&PublicKey::from(auth_granted.app_keys.bls_pk)));
let auth = unwrap!(Authenticator::login(
locator.clone(),
password.clone(),
|| (),
));
revoke(&auth, &app_id);
let app_id = app_id.clone();
unwrap!(run(&auth, move |client| {
verify_app_is_revoked(client, app_id, ac_entries)
}));
}
#[test]
fn app_authentication_during_pending_revocation() {
let (auth, locator, password) = create_authenticator();
let auth_req = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let app_id = auth_req.app.id.clone();
let _ = unwrap!(register_app(&auth, &auth_req));
simulate_revocation_failure(&locator, &password, iter::once(&app_id));
match register_app(&auth, &auth_req) {
Err(AuthError::PendingRevocation) => (),
x => panic!("Unexpected: {:?}", x),
}
revoke(&auth, &app_id);
let _ = unwrap!(register_app(&auth, &auth_req));
}
#[test]
fn flushing_app_revocation_queue() {
let (auth, locator, password) = create_authenticator();
let auth_req = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let _ = unwrap!(register_app(&auth, &auth_req));
let app_id_0 = auth_req.app.id.clone();
let auth_req = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let _ = unwrap!(register_app(&auth, &auth_req));
let app_id_1 = auth_req.app.id.clone();
simulate_revocation_failure(&locator, &password, &[&app_id_0, &app_id_1]);
{
let app_id_0 = app_id_0.clone();
let app_id_1 = app_id_1.clone();
unwrap!(run(&auth, |client| {
let client = client.clone();
config::list_apps(&client)
.then(move |res| {
let (_, apps) = unwrap!(res);
let f_0 = app_state(&client, &apps, &app_id_0);
let f_1 = app_state(&client, &apps, &app_id_1);
f_0.join(f_1)
})
.then(|res| {
let (state_0, state_1) = unwrap!(res);
assert_eq!(state_0, AppState::Authenticated);
assert_eq!(state_1, AppState::Authenticated);
Ok(())
})
}))
}
let auth = unwrap!(Authenticator::login(locator, password, || ()));
unsafe {
unwrap!(call_0(|ud, cb| auth_flush_app_revocation_queue(
&auth, ud, cb
),))
}
unwrap!(run(&auth, |client| {
let c2 = client.clone();
config::list_apps(client)
.then(move |res| {
let (_, apps) = unwrap!(res);
let f_0 = app_state(&c2, &apps, &app_id_0);
let f_1 = app_state(&c2, &apps, &app_id_1);
f_0.join(f_1)
})
.then(move |res| {
let (state_0, state_1) = unwrap!(res);
assert_eq!(state_0, AppState::Revoked);
assert_eq!(state_1, AppState::Revoked);
Ok(())
})
}))
}
#[test]
fn concurrent_revocation_of_single_app() {
let rng = SeededRng::new();
let concurrency = 2;
let (auth, locator, password) = create_authenticator();
let mut containers_req = HashMap::new();
let _ = containers_req.insert(
"_documents".to_owned(),
btree_set![
Permission::Read,
Permission::Insert,
Permission::Update,
Permission::Delete,
],
);
let (app_id_0, auth_granted_0) =
unwrap!(register_rand_app(&auth, true, containers_req.clone()));
let (app_id_1, _) = unwrap!(register_rand_app(&auth, true, containers_req));
let ac_entries_0 = access_container(&auth, app_id_0.clone(), auth_granted_0);
let info = unwrap!(get_container_from_authenticator_entry(&auth, "_documents"));
unwrap!(create_file(&auth, info, "shared.txt", vec![0; 10], true));
for app_id in &[&app_id_0, &app_id_1] {
let info = unwrap!(get_container_from_authenticator_entry(
&auth,
&app_container_name(app_id),
));
unwrap!(create_file(&auth, info, "private.txt", vec![0; 10], true));
}
let barrier = Arc::new(Barrier::new(concurrency));
let sync = Synchronizer::new(rng);
let join_handles: Vec<_> = (0..concurrency)
.map(|_| {
let locator = locator.clone();
let password = password.clone();
let app_id = app_id_0.clone();
let barrier = Arc::clone(&barrier);
let sync = sync.clone();
thread::spawn(move || {
let auth = unwrap!(Authenticator::login_with_hook(
locator,
password,
|| (),
move |routing| sync.hook(routing),
));
let _ = barrier.wait();
try_revoke(&auth, &app_id)
})
})
.collect();
let success =
join_handles
.into_iter()
.fold(false, |success, handle| match unwrap!(handle.join()) {
Ok(_) | Err(AuthError::IpcError(IpcError::UnknownApp)) => true,
_ => success,
});
if !success {
unwrap!(try_revoke(&auth, &app_id_0));
}
unwrap!(run(&auth, move |client| {
let app_0 = verify_app_is_revoked(client, app_id_0, ac_entries_0);
let app_1 = verify_app_is_authenticated(client, app_id_1);
app_0.join(app_1).map(|_| ())
}));
}
#[test]
fn concurrent_revocation_of_multiple_apps() {
let rng = SeededRng::new();
let (auth, locator, password) = create_authenticator();
let mut containers_req = HashMap::new();
let _ = containers_req.insert(
"_documents".to_owned(),
btree_set![
Permission::Read,
Permission::Insert,
Permission::Update,
Permission::Delete,
],
);
let (app_id_0, auth_granted_0) =
unwrap!(register_rand_app(&auth, true, containers_req.clone()));
let (app_id_1, auth_granted_1) =
unwrap!(register_rand_app(&auth, true, containers_req.clone()));
let (app_id_2, _) = unwrap!(register_rand_app(&auth, true, containers_req));
let ac_entries_0 = access_container(&auth, app_id_0.clone(), auth_granted_0);
let ac_entries_1 = access_container(&auth, app_id_1.clone(), auth_granted_1);
let info = unwrap!(get_container_from_authenticator_entry(&auth, "_documents"));
unwrap!(create_file(&auth, info, "shared.txt", vec![0; 10], true));
for app_id in &[&app_id_0, &app_id_1, &app_id_2] {
let info = unwrap!(get_container_from_authenticator_entry(
&auth,
&app_container_name(app_id),
));
unwrap!(create_file(&auth, info, "private.txt", vec![0; 10], true));
}
let apps_to_revoke = [app_id_0.clone(), app_id_1.clone()];
let barrier = Arc::new(Barrier::new(apps_to_revoke.len()));
let sync = Synchronizer::new(rng);
let join_handles: Vec<_> = apps_to_revoke
.iter()
.map(|app_id| {
let locator = locator.clone();
let password = password.clone();
let app_id = app_id.to_string();
let barrier = Arc::clone(&barrier);
let sync = sync.clone();
thread::spawn(move || {
let auth = unwrap!(Authenticator::login_with_hook(
locator,
password,
|| (),
move |routing| sync.hook(routing),
));
let _ = barrier.wait();
try_revoke(&auth, &app_id)
})
})
.collect();
let results: Vec<_> = join_handles
.into_iter()
.map(|handle| unwrap!(handle.join()))
.collect();
for (app_id, result) in apps_to_revoke.iter().zip(results) {
if result.is_err() {
match try_revoke(&auth, app_id) {
Ok(_) | Err(AuthError::IpcError(IpcError::UnknownApp)) => (),
Err(error) => panic!("Unexpected revocation failure: {:?}", error),
}
}
}
unwrap!(run(&auth, move |client| {
let app_0 = verify_app_is_revoked(client, app_id_0, ac_entries_0);
let app_1 = verify_app_is_revoked(client, app_id_1, ac_entries_1);
let app_2 = verify_app_is_authenticated(client, app_id_2);
app_0.join3(app_1, app_2).map(|_| ())
}));
}
fn simulate_revocation_failure<T, S>(locator: &str, password: &str, app_ids: T)
where
T: IntoIterator<Item = S>,
S: AsRef<str>,
{
let auth = unwrap!(Authenticator::login(locator, password, || ()));
let ac_info = unwrap!(run(&auth, |client| Ok(client.access_container())));
let auth = unwrap!(Authenticator::login_with_hook(
locator,
password,
|| (),
move |mut cm| -> ConnectionManager {
let ac_info = ac_info.clone();
cm.set_request_hook(move |request| match *request {
Request::DelMDataUserPermissions { address, .. } => {
if *address.name() == ac_info.name() && address.tag() == ac_info.type_tag()
{
Some(Response::Mutation(Err(SndError::InsufficientBalance)))
} else {
None
}
}
_ => None,
});
cm
},
));
for app_id in app_ids {
match try_revoke(&auth, app_id.as_ref()) {
Err(_) => (),
x => panic!("Unexpected {:?}", x),
}
}
}
}
#[test]
fn app_revocation_and_reauth() {
let authenticator = create_account_and_login();
let auth_req1 = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let app_id1 = auth_req1.app.id.clone();
let auth_granted1 = unwrap!(register_app(&authenticator, &auth_req1));
let auth_req2 = AuthReq {
app: rand_app(),
app_container: true,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let app_id2 = auth_req2.app.id.clone();
let auth_granted2 = unwrap!(register_app(&authenticator, &auth_req2));
let mut ac_entries = access_container(&authenticator, app_id1.clone(), auth_granted1.clone());
let (videos_md1, _) = unwrap!(ac_entries.remove("_videos"));
unwrap!(create_file(
&authenticator,
videos_md1.clone(),
"1.mp4",
vec![1; 10],
true,
));
let mut ac_entries = access_container(&authenticator, app_id2.clone(), auth_granted2.clone());
let (videos_md2, _) = unwrap!(ac_entries.remove("_videos"));
unwrap!(create_file(
&authenticator,
videos_md2.clone(),
"2.mp4",
vec![1; 10],
true,
));
let app_container_name = app_container_name(&app_id2);
let (app_container_md, _) = unwrap!(ac_entries.remove(&app_container_name));
unwrap!(create_file(
&authenticator,
app_container_md.clone(),
"3.mp4",
vec![1; 10],
true,
));
assert_eq!(count_mdata_entries(&authenticator, videos_md1.clone()), 2);
let _ = unwrap!(fetch_file(&authenticator, videos_md1.clone(), "1.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md1.clone(), "2.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "1.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "2.mp4"));
revoke(&authenticator, &app_id1);
assert_eq!(count_mdata_entries(&authenticator, videos_md1.clone()), 2);
let ac = try_access_container(&authenticator, app_id1.clone(), auth_granted1.clone());
assert!(ac.is_none());
let (name, tag) = (videos_md2.name(), videos_md2.type_tag());
let perms = unwrap!(run(&authenticator, move |client| {
client
.list_mdata_permissions(MDataAddress::Seq { name, tag })
.map_err(From::from)
}));
assert!(!perms.contains_key(&PublicKey::from(auth_granted1.app_keys.bls_pk)));
assert!(perms.contains_key(&PublicKey::from(auth_granted2.app_keys.bls_pk)));
let (app_id1_clone, app_id2_clone) = (app_id1.clone(), app_id2.clone());
unwrap!(run(&authenticator, move |client| {
let app_1 = verify_app_is_revoked(client, app_id1_clone, ac_entries);
let app_2 = verify_app_is_authenticated(client, app_id2_clone);
app_1.join(app_2).map(|_| ())
}));
let mut ac_entries = access_container(&authenticator, app_id2.clone(), auth_granted2.clone());
let (videos_md2, _) = unwrap!(ac_entries.remove("_videos"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "1.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "2.mp4"));
let auth_granted1 = unwrap!(register_app(&authenticator, &auth_req1));
let mut ac_entries = access_container(&authenticator, app_id1.clone(), auth_granted1.clone());
let (videos_md1, _) = unwrap!(ac_entries.remove("_videos"));
let _ = unwrap!(fetch_file(&authenticator, videos_md1.clone(), "1.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md1.clone(), "2.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "1.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "2.mp4"));
revoke(&authenticator, &app_id1);
assert_eq!(count_mdata_entries(&authenticator, videos_md1.clone()), 2);
let (app_id1_clone, app_id2_clone) = (app_id1.clone(), app_id2.clone());
unwrap!(run(&authenticator, move |client| {
let app_1 = verify_app_is_revoked(client, app_id1_clone, ac_entries);
let app_2 = verify_app_is_authenticated(client, app_id2_clone);
app_1.join(app_2).map(|_| ())
}));
let mut ac_entries = access_container(&authenticator, app_id2.clone(), auth_granted2.clone());
let (videos_md2, _) = unwrap!(ac_entries.remove("_videos"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "1.mp4"));
let _ = unwrap!(fetch_file(&authenticator, videos_md2.clone(), "2.mp4"));
revoke(&authenticator, &app_id2);
let (app_id1_clone, app_id2_clone) = (app_id1.clone(), app_id2.clone());
unwrap!(run(&authenticator, move |client| {
let app_1 = verify_app_is_revoked(client, app_id1_clone, ac_entries.clone());
let app_2 = verify_app_is_revoked(client, app_id2_clone, ac_entries);
app_1.join(app_2).map(|_| ())
}));
let auth_granted2 = unwrap!(register_app(&authenticator, &auth_req2));
let mut ac_entries = access_container(&authenticator, app_id2.clone(), auth_granted2.clone());
let (app_container_md, _) = unwrap!(ac_entries.remove(&app_container_name));
assert_eq!(
count_mdata_entries(&authenticator, app_container_md.clone()),
1
);
let _ = unwrap!(fetch_file(
&authenticator,
app_container_md.clone(),
"3.mp4",
));
let app_id2_clone = app_id2.clone();
unwrap!(run(&authenticator, move |client| {
verify_app_is_authenticated(client, app_id2_clone)
}));
revoke(&authenticator, &app_id2);
let app_id2_clone = app_id2.clone();
unwrap!(run(&authenticator, move |client| {
verify_app_is_revoked(client, app_id2_clone, ac_entries)
}));
}
#[test]
#[ignore]
fn revocation_symmetric_decipher_failure() {
let authenticator = create_account_and_login();
let mut corrupt_containers = HashMap::new();
let _ = corrupt_containers.insert(
"_downloads".to_owned(),
btree_set![Permission::Read, Permission::Insert],
);
let auth_req1 = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let app_id1 = auth_req1.app.id.clone();
debug!("Registering app 1 with ID {}...", app_id1);
let auth_granted1 = unwrap!(register_app(&authenticator, &auth_req1));
let auth_req2 = AuthReq {
app: rand_app(),
app_container: true,
app_permissions: Default::default(),
containers: corrupt_containers,
};
let app_id2 = auth_req2.app.id.clone();
debug!("Registering app 2 with ID {}...", app_id2);
let auth_granted2 = unwrap!(register_app(&authenticator, &auth_req2));
let auth_req3 = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let app_id3 = auth_req3.app.id.clone();
debug!("Registering app 3 with ID {}...", app_id3);
let _auth_granted3 = unwrap!(register_app(&authenticator, &auth_req3));
let mut ac_entries = access_container(&authenticator, app_id2.clone(), auth_granted2.clone());
let (downloads_md, _) = unwrap!(ac_entries.remove("_downloads"));
unwrap!(create_file(
&authenticator,
downloads_md.clone(),
"video.mp4",
vec![1; 10],
true,
));
{
let app_id1 = app_id1.clone();
let app_id2 = app_id2.clone();
let app_id2_clone = app_id2.clone();
unwrap!(run(&authenticator, move |client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
get_app_revocation_queue(client)
.map(move |(version, queue)| {
let _ = push_to_app_revocation_queue(
&c2,
queue,
config::next_version(version),
&app_id1,
);
})
.and_then(move |_| {
get_app_revocation_queue(&c3).map(move |(version, queue)| {
let _ = push_to_app_revocation_queue(
&c4,
queue,
config::next_version(version),
&app_id2_clone,
);
})
})
.and_then(move |_| corrupt_container(&c5, "_downloads"))
}));
}
match try_revoke(&authenticator, &app_id3) {
Ok(()) => (),
Err(AuthError::CoreError(CoreError::SymmetricDecipherFailure)) => (),
Err(x) => panic!("An unexpected error occurred: {:?}", x),
}
let queue = unwrap!(run(&authenticator, move |client| {
get_app_revocation_queue(client).map(|(_, queue)| queue)
}));
let ac = try_access_container(&authenticator, app_id1.clone(), auth_granted1.clone());
assert!(ac.is_none());
assert!(!queue.contains(&app_id1));
assert!(!queue.contains(&app_id2));
assert!(!queue.contains(&app_id3));
}
#[test]
fn flushing_empty_app_revocation_queue_does_not_mutate_network() {
let (auth, ..) = create_authenticator();
let balance_0 = unwrap!(run(&auth, |client| {
client.get_balance(None).map_err(AuthError::from)
}));
unwrap!(run(&auth, |client| {
revocation::flush_app_revocation_queue(client)
}));
let balance_1 = unwrap!(run(&auth, |client| {
client.get_balance(None).map_err(AuthError::from)
}));
assert_eq!(balance_0, balance_1);
let auth_req = AuthReq {
app: rand_app(),
app_container: false,
app_permissions: Default::default(),
containers: create_containers_req(),
};
let _ = unwrap!(register_app(&auth, &auth_req));
let app_id = auth_req.app.id;
revoke(&auth, &app_id);
let balance_2 = unwrap!(run(&auth, |client| {
client.get_balance(None).map_err(AuthError::from)
}));
unwrap!(run(&auth, |client| {
revocation::flush_app_revocation_queue(client)
}));
let balance_3 = unwrap!(run(&auth, |client| {
client.get_balance(None).map_err(AuthError::from)
}));
assert_eq!(balance_2, balance_3);
}
#[test]
fn revocation_with_unencrypted_container_entries() {
let (auth, ..) = create_authenticator();
let mut containers_req = HashMap::new();
let _ = containers_req.insert(
"_documents".to_owned(),
btree_set![Permission::Read, Permission::Insert,],
);
let (app_id, _) = unwrap!(register_rand_app(&auth, true, containers_req));
let shared_info = unwrap!(get_container_from_authenticator_entry(&auth, "_documents"));
let shared_info2 = shared_info.clone();
let shared_key = b"shared-key".to_vec();
let shared_content = b"shared-value".to_vec();
let shared_actions =
MDataSeqEntryActions::new().ins(shared_key.clone(), shared_content.clone(), 0);
let dedicated_info = unwrap!(get_container_from_authenticator_entry(
&auth,
&app_container_name(&app_id),
));
let dedicated_info2 = dedicated_info.clone();
let dedicated_key = b"dedicated-key".to_vec();
let dedicated_content = b"dedicated-value".to_vec();
let dedicated_actions =
MDataSeqEntryActions::new().ins(dedicated_key.clone(), dedicated_content.clone(), 0);
unwrap!(run(&auth, move |client| {
let f0 = client.mutate_seq_mdata_entries(
shared_info.name(),
shared_info.type_tag(),
shared_actions,
);
let f1 = client.mutate_seq_mdata_entries(
dedicated_info.name(),
dedicated_info.type_tag(),
dedicated_actions,
);
f0.join(f1).map(|_| ()).map_err(AuthError::from)
}));
revoke(&auth, &app_id);
unwrap!(run(&auth, move |client| {
let f0 =
client.get_seq_mdata_value(shared_info2.name(), shared_info2.type_tag(), shared_key);
let f1 = client.get_seq_mdata_value(
dedicated_info2.name(),
dedicated_info2.type_tag(),
dedicated_key,
);
f0.join(f1).then(move |res| {
let (shared_value, dedicated_value) = unwrap!(res);
assert_eq!(shared_value.data, shared_content);
assert_eq!(dedicated_value.data, dedicated_content);
Ok(())
})
}))
}
fn count_mdata_entries(authenticator: &Authenticator, info: MDataInfo) -> usize {
unwrap!(run(authenticator, move |client| {
client
.list_seq_mdata_entries(info.name(), info.type_tag())
.map(|entries| entries.len())
.map_err(From::from)
}))
}