use crate::apps::{
apps_accessing_mutable_data, list_registered, list_revoked, remove_revoked_app,
RegisteredApp as NativeRegisteredApp,
};
use crate::ffi::errors::{Error, Result};
use crate::Authenticator;
use ffi_utils::call_result_cb;
use ffi_utils::{
catch_unwind_cb, vec_from_raw_parts, FfiResult, OpaqueCtx, ReprC, SafePtr, FFI_RESULT_OK,
};
use futures::Future;
use safe_core::core_structs::AppAccess as NativeAppAccess;
use safe_core::ffi::arrays::XorNameArray;
use safe_core::ffi::ipc::req::{AppExchangeInfo, ContainerPermissions};
use safe_core::ffi::ipc::resp::AppAccess;
use safe_core::ipc::req::AppExchangeInfo as NativeAppExchangeInfo;
use safe_core::FutureExt;
use safe_nd::XorName;
use std::os::raw::{c_char, c_void};
#[repr(C)]
pub struct RegisteredApp {
pub app_info: AppExchangeInfo,
pub containers: *const ContainerPermissions,
pub containers_len: usize,
pub app_permissions: AppPermissions,
}
impl Drop for RegisteredApp {
fn drop(&mut self) {
unsafe {
let _ = vec_from_raw_parts(
self.containers as *mut ContainerPermissions,
self.containers_len,
);
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct AppPermissions {
pub transfer_coins: bool,
pub perform_mutations: bool,
pub get_balance: bool,
}
#[no_mangle]
pub unsafe extern "C" fn auth_rm_revoked_app(
auth: *const Authenticator,
app_id: *const c_char,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void, result: *const FfiResult),
) {
let user_data = OpaqueCtx(user_data);
catch_unwind_cb(user_data.0, o_cb, || -> Result<_> {
let app_id = String::clone_from_repr_c(app_id)?;
(*auth).send(move |client| {
remove_revoked_app(client, app_id)
.then(move |res| {
call_result_cb!(res.map_err(Error::from), user_data, o_cb);
Ok(())
})
.into_box()
.into()
})
});
}
#[no_mangle]
pub unsafe extern "C" fn auth_revoked_apps(
auth: *const Authenticator,
user_data: *mut c_void,
o_cb: extern "C" fn(
user_data: *mut c_void,
result: *const FfiResult,
app_exchange_info: *const AppExchangeInfo,
app_exchange_info_len: usize,
),
) {
let user_data = OpaqueCtx(user_data);
catch_unwind_cb(user_data.0, o_cb, || -> Result<_> {
(*auth).send(move |client| {
list_revoked(client)
.and_then(move |apps| {
let app_list: Vec<_> = apps
.into_iter()
.map(NativeAppExchangeInfo::into_repr_c)
.collect::<std::result::Result<_, _>>()?;
o_cb(
user_data.0,
FFI_RESULT_OK,
app_list.as_safe_ptr(),
app_list.len(),
);
Ok(())
})
.map_err(move |e| {
call_result_cb!(Err::<(), _>(Error::from(e)), user_data, o_cb);
})
.into_box()
.into()
})?;
Ok(())
});
}
#[no_mangle]
pub unsafe extern "C" fn auth_registered_apps(
auth: *const Authenticator,
user_data: *mut c_void,
o_cb: extern "C" fn(
user_data: *mut c_void,
result: *const FfiResult,
registered_app: *const RegisteredApp,
registered_app_len: usize,
),
) {
let user_data = OpaqueCtx(user_data);
catch_unwind_cb(user_data.0, o_cb, || -> Result<_> {
(*auth).send(move |client| {
list_registered(client)
.and_then(move |registered_apps| {
let apps: Vec<_> = registered_apps
.into_iter()
.map(NativeRegisteredApp::into_repr_c)
.collect::<std::result::Result<_, _>>()?;
o_cb(user_data.0, FFI_RESULT_OK, apps.as_safe_ptr(), apps.len());
Ok(())
})
.map_err(move |e| {
call_result_cb!(Err::<(), _>(Error::from(e)), user_data, o_cb);
})
.into_box()
.into()
})?;
Ok(())
})
}
#[no_mangle]
pub unsafe extern "C" fn auth_apps_accessing_mutable_data(
auth: *const Authenticator,
md_name: *const XorNameArray,
md_type_tag: u64,
user_data: *mut c_void,
o_cb: extern "C" fn(
user_data: *mut c_void,
result: *const FfiResult,
app_access: *const AppAccess,
app_access_len: usize,
),
) {
let user_data = OpaqueCtx(user_data);
let name = XorName(*md_name);
catch_unwind_cb(user_data.0, o_cb, || -> Result<_> {
(*auth).send(move |client| {
apps_accessing_mutable_data(client, name, md_type_tag)
.and_then(move |apps_with_access| {
let app_access_vec: Vec<_> = apps_with_access
.into_iter()
.map(NativeAppAccess::into_repr_c)
.collect::<std::result::Result<_, _>>()?;
o_cb(
user_data.0,
FFI_RESULT_OK,
app_access_vec.as_safe_ptr(),
app_access_vec.len(),
);
Ok(())
})
.map_err(move |e| {
call_result_cb!(Err::<(), _>(Error::from(e)), user_data, o_cb);
})
.into_box()
.into()
})?;
Ok(())
})
}
#[cfg(test)]
mod tests {
use crate::app_auth::{app_state, AppState};
use crate::app_container::fetch;
use crate::errors::AuthError;
use crate::ffi::errors::{ERR_UNEXPECTED, ERR_UNKNOWN_APP};
use crate::revocation::revoke_app;
use crate::test_utils::{
create_account_and_login, create_file, fetch_file, get_app_or_err, rand_app, register_app,
};
use crate::{config, run};
use ffi_utils::test_utils::call_0;
use safe_core::ipc::{AuthReq, IpcError};
use std::collections::HashMap;
use unwrap::unwrap;
use super::*;
#[test]
fn rm_revoked_nonexisting() {
let auth = create_account_and_login();
let app_info = rand_app();
let app_info_ffi = unwrap!(app_info.into_repr_c());
let result =
unsafe { call_0(|ud, cb| auth_rm_revoked_app(&auth, app_info_ffi.id, ud, cb)) };
match result {
Err(ERR_UNKNOWN_APP) => (),
Err(x) => panic!("Unexpected {:?}", x),
Ok(()) => panic!("Unexpected successful removal of non-existing app"),
};
}
#[test]
fn rm_revoked_authorised() {
let auth = create_account_and_login();
let app_info = rand_app();
let app_id = app_info.id.clone();
let _ = unwrap!(register_app(
&auth,
&AuthReq {
app: app_info.clone(),
app_container: false,
app_permissions: Default::default(),
containers: HashMap::new(),
},
));
let app_info_ffi = unwrap!(app_info.into_repr_c());
let result =
unsafe { call_0(|ud, cb| auth_rm_revoked_app(&auth, app_info_ffi.id, ud, cb)) };
match result {
Err(ERR_UNEXPECTED) => (),
Err(x) => panic!("Unexpected {:?}", x),
Ok(()) => panic!("Unexpected successful removal of non-existing app"),
};
unwrap!(run(&auth, |client| {
let c2 = client.clone();
config::list_apps(client)
.and_then(move |(_, apps)| app_state(&c2, &apps, &app_id))
.and_then(move |res| match res {
AppState::Authenticated => Ok(()),
_ => panic!("App state changed after failed revocation"),
})
}));
}
#[test]
fn rm_revoked_complete() {
let auth = create_account_and_login();
let app_info = rand_app();
let app_id = app_info.id.clone();
let app_id2 = app_id.clone();
let app_id3 = app_id.clone();
let app_id4 = app_id.clone();
let app_id5 = app_id.clone();
let auth_granted1 = unwrap!(register_app(
&auth,
&AuthReq {
app: app_info.clone(),
app_container: true,
app_permissions: Default::default(),
containers: HashMap::new(),
},
));
let mdata_info = unwrap!({ unwrap!(run(&auth, move |client| fetch(client, &app_id3))) });
unwrap!(create_file(
&auth,
mdata_info.clone(),
"test",
vec![1; 10],
true
));
{
unwrap!(run(&auth, move |client| revoke_app(client, &app_id2)))
}
assert!(get_app_or_err(&auth, &app_id).is_ok());
{
unwrap!(run(&auth, move |client| {
fetch(client, &app_id4).and_then(move |res| match res {
Some(_) => Ok(()),
None => panic!("App container not accessible"),
})
}))
}
let app_info_ffi = unwrap!(app_info.clone().into_repr_c());
unsafe {
unwrap!(call_0(|ud, cb| auth_rm_revoked_app(
&auth,
app_info_ffi.id,
ud,
cb
),))
};
let res = get_app_or_err(&auth, &app_id);
match res {
Err(AuthError::IpcError(IpcError::UnknownApp)) => (),
Err(x) => panic!("Unexpected {:?}", x),
Ok(_) => panic!("App still listed in the authenticator config"),
};
let res = fetch_file(&auth, mdata_info, "test");
match res {
Err(_) => (),
Ok(_) => panic!("File not removed"),
}
let auth_granted2 = unwrap!(register_app(
&auth,
&AuthReq {
app: app_info,
app_container: true,
app_permissions: Default::default(),
containers: HashMap::new(),
},
));
assert!(get_app_or_err(&auth, &app_id).is_ok());
assert_ne!(auth_granted1.app_keys, auth_granted2.app_keys);
let mdata_info = unwrap!({ unwrap!(run(&auth, move |client| fetch(client, &app_id5))) });
let res = fetch_file(&auth, mdata_info, "test");
match res {
Err(_) => (),
Ok(_) => panic!("File not removed"),
}
}
}