use Authenticator;
use access_container;
use app_auth;
use config;
use errors::AuthError;
use futures::{Future, IntoFuture};
use futures::future;
use ipc::decode_ipc_msg;
use rand::{self, Rng};
use revocation;
use routing::User;
use rust_sodium::crypto::sign;
use safe_core::{Client, FutureExt, MDataInfo};
#[cfg(feature = "use-mock-routing")]
use safe_core::MockRouting;
use safe_core::ipc::{self, AppExchangeInfo, AuthGranted, AuthReq, IpcMsg, IpcReq};
use safe_core::ipc::req::{ContainerPermissions, container_perms_into_permission_set};
use safe_core::ipc::resp::AccessContainerEntry;
use safe_core::nfs::{File, Mode, file_helper};
use std::collections::HashMap;
use std::sync::mpsc;
#[macro_export]
macro_rules! assert_match {
($e:expr, $p:pat) => {
match $e {
$p => (),
x => panic!("Unexpected {:?} (expecting {})", x, stringify!($p)),
}
}
}
pub fn create_authenticator() -> (Authenticator, String, String) {
let mut rng = rand::thread_rng();
let locator: String = rng.gen_ascii_chars().take(10).collect();
let password: String = rng.gen_ascii_chars().take(10).collect();
let invitation: String = rng.gen_ascii_chars().take(10).collect();
let auth = unwrap!(Authenticator::create_acc(
locator.clone(),
password.clone(),
invitation,
|| (),
));
(auth, locator, password)
}
pub fn create_account_and_login() -> Authenticator {
let (_, locator, password) = create_authenticator();
unwrap!(Authenticator::login(locator, password, || ()))
}
pub fn try_revoke(authenticator: &Authenticator, app_id: &str) -> Result<(), AuthError> {
let app_id = app_id.to_string();
try_run(authenticator, move |client| {
revocation::revoke_app(client, &app_id)
})
}
pub fn revoke(authenticator: &Authenticator, app_id: &str) {
match try_revoke(authenticator, app_id) {
Ok(_) => (),
x => panic!("Unexpected {:?}", x),
}
}
#[cfg(all(any(test, feature = "testing"), feature = "use-mock-routing"))]
pub fn create_account_and_login_with_hook<F>(hook: F) -> Authenticator
where
F: Fn(MockRouting) -> MockRouting + Send + 'static,
{
let (_, locator, password) = create_authenticator();
unwrap!(Authenticator::login_with_hook(
locator,
password,
|| (),
hook,
))
}
pub fn get_app_or_err(
authenticator: &Authenticator,
app_id: &str,
) -> Result<config::AppInfo, AuthError> {
let app_id = app_id.to_string();
try_run(
authenticator,
move |client| config::get_app(client, &app_id),
)
}
pub fn register_app(
authenticator: &Authenticator,
auth_req: &AuthReq,
) -> Result<AuthGranted, AuthError> {
let req_id = ipc::gen_req_id();
let msg = IpcMsg::Req {
req_id: req_id,
req: IpcReq::Auth(auth_req.clone()),
};
let ipc_req = run(authenticator, move |client| decode_ipc_msg(client, msg));
match ipc_req {
Ok(IpcMsg::Req { req: IpcReq::Auth(_), .. }) => (),
x => return Err(AuthError::Unexpected(format!("Unexpected {:?}", x))),
}
let auth_req = auth_req.clone();
try_run(authenticator, move |client| {
app_auth::authenticate(client, auth_req)
})
}
pub fn register_rand_app(
authenticator: &Authenticator,
app_container: bool,
containers_req: HashMap<String, ContainerPermissions>,
) -> Result<(String, AuthGranted), AuthError> {
let auth_req = AuthReq {
app: rand_app(),
app_container: app_container,
containers: containers_req,
};
let auth_granted = register_app(authenticator, &auth_req)?;
let app_id = auth_req.app.id;
Ok((app_id, auth_granted))
}
pub fn try_run<F, I, T>(authenticator: &Authenticator, f: F) -> Result<T, AuthError>
where
F: FnOnce(&Client<()>) -> I + Send + 'static,
I: IntoFuture<Item = T, Error = AuthError> + 'static,
T: Send + 'static,
{
let (tx, rx) = mpsc::channel();
unwrap!(authenticator.send(move |client| {
let future = f(client)
.into_future()
.then(move |result| {
unwrap!(tx.send(result));
Ok(())
})
.into_box();
Some(future)
}));
unwrap!(rx.recv())
}
pub fn run<F, I, T>(authenticator: &Authenticator, f: F) -> T
where
F: FnOnce(&Client<()>) -> I + Send + 'static,
I: IntoFuture<Item = T, Error = AuthError> + 'static,
T: Send + 'static,
{
unwrap!(try_run(authenticator, f))
}
pub fn rand_app() -> AppExchangeInfo {
let mut rng = rand::thread_rng();
AppExchangeInfo {
id: rng.gen_ascii_chars().take(10).collect(),
scope: None,
name: rng.gen_ascii_chars().take(10).collect(),
vendor: rng.gen_ascii_chars().take(10).collect(),
}
}
pub fn create_file<T: Into<String>>(
authenticator: &Authenticator,
container_info: MDataInfo,
name: T,
content: Vec<u8>,
) -> Result<(), AuthError> {
let name = name.into();
try_run(authenticator, |client| {
let c2 = client.clone();
file_helper::write(
client.clone(),
File::new(vec![]),
Mode::Overwrite,
container_info.enc_key().cloned(),
).then(move |res| {
let writer = unwrap!(res);
writer.write(&content).and_then(move |_| writer.close())
})
.then(move |file| {
file_helper::insert(c2, container_info, name, &unwrap!(file))
})
.map_err(From::from)
})
}
pub fn fetch_file<T: Into<String>>(
authenticator: &Authenticator,
container_info: MDataInfo,
name: T,
) -> Result<File, AuthError> {
let name = name.into();
try_run(authenticator, |client| {
file_helper::fetch(client.clone(), container_info, name)
.map(|(_, file)| file)
.map_err(From::from)
})
}
pub fn access_container<S: Into<String>>(
authenticator: &Authenticator,
app_id: S,
auth_granted: AuthGranted,
) -> AccessContainerEntry {
unwrap!(
try_access_container(authenticator, app_id, auth_granted),
"Access container entry is empty"
)
}
pub fn try_access_container<S: Into<String>>(
authenticator: &Authenticator,
app_id: S,
auth_granted: AuthGranted,
) -> Option<AccessContainerEntry> {
let app_keys = auth_granted.app_keys;
let app_id = app_id.into();
run(authenticator, move |client| {
access_container::fetch_entry(client, &app_id, app_keys).map(move |(_, entry)| entry)
})
}
pub fn get_container_from_authenticator_entry(
authenticator: &Authenticator,
container: &str,
) -> Result<MDataInfo, AuthError> {
let container = String::from(container);
try_run(authenticator, move |client| {
access_container::fetch_authenticator_entry(client).and_then(move |(_, mut ac_entries)| {
ac_entries.remove(&container).ok_or_else(|| {
AuthError::from(format!("'{}' not found in the access container", container))
})
})
})
}
pub fn compare_access_container_entries(
authenticator: &Authenticator,
app_sign_pk: sign::PublicKey,
mut access_container: AccessContainerEntry,
expected: HashMap<String, ContainerPermissions>,
) {
let results = run(authenticator, move |client| {
let mut reqs = Vec::new();
let user = User::Key(app_sign_pk);
for (container, expected_perms) in expected {
let expected_perm_set = container_perms_into_permission_set(&expected_perms);
let (md_info, perms) = unwrap!(
access_container.remove(&container),
"No '{}' in access container {:?}",
container,
access_container
);
assert_eq!(perms, expected_perms);
let fut = client
.list_mdata_user_permissions(md_info.name, md_info.type_tag, user)
.map(move |perms| (perms, expected_perm_set));
reqs.push(fut);
}
future::join_all(reqs).map_err(AuthError::from)
});
for (perms, expected) in results {
assert_eq!(perms, expected);
}
}