#![allow(unsafe_code)]
mod auth;
mod containers;
mod share_mdata;
pub use self::auth::AuthReq;
pub use self::containers::ContainersReq;
pub use self::share_mdata::{ShareMData, ShareMDataReq};
use crate::ffi::ipc::req::{
AppExchangeInfo as FfiAppExchangeInfo, ContainerPermissions as FfiContainerPermissions,
PermissionSet as FfiPermissionSet,
};
use crate::ipc::errors::IpcError;
use ffi_utils::{ReprC, StringError};
use safe_nd::{MDataAction, MDataPermissionSet};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeSet, HashMap};
use std::ffi::{CString, NulError};
use std::{ptr, slice};
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Permission {
Read,
Insert,
Update,
Delete,
ManagePermissions,
}
pub type ContainerPermissions = BTreeSet<Permission>;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum IpcReq {
Auth(AuthReq),
Containers(ContainersReq),
Unregistered(Vec<u8>),
ShareMData(ShareMDataReq),
}
pub fn containers_into_vec<ContainersIter>(
containers: ContainersIter,
) -> Result<Vec<FfiContainerPermissions>, NulError>
where
ContainersIter: IntoIterator<Item = (String, ContainerPermissions)>,
{
containers
.into_iter()
.map(|(cont_name, access)| {
Ok(FfiContainerPermissions {
cont_name: CString::new(cont_name)?.into_raw(),
access: container_perms_into_repr_c(&access),
})
})
.collect()
}
pub fn container_perms_into_repr_c(perms: &ContainerPermissions) -> FfiPermissionSet {
let mut output = FfiPermissionSet::default();
for perm in perms {
match *perm {
Permission::Read => {
output.read = true;
}
Permission::Insert => {
output.insert = true;
}
Permission::Update => {
output.update = true;
}
Permission::Delete => {
output.delete = true;
}
Permission::ManagePermissions => output.manage_permissions = true,
}
}
output
}
pub fn container_perms_from_repr_c(
perms: FfiPermissionSet,
) -> Result<ContainerPermissions, IpcError> {
let mut output = BTreeSet::new();
if perms.read {
let _ = output.insert(Permission::Read);
}
if perms.insert {
let _ = output.insert(Permission::Insert);
}
if perms.update {
let _ = output.insert(Permission::Update);
}
if perms.delete {
let _ = output.insert(Permission::Delete);
}
if perms.manage_permissions {
let _ = output.insert(Permission::ManagePermissions);
}
if output.is_empty() {
Err(IpcError::from("No permissions were provided"))
} else {
Ok(output)
}
}
pub fn container_perms_into_permission_set<'a, Iter>(permissions: Iter) -> MDataPermissionSet
where
Iter: IntoIterator<Item = &'a Permission>,
{
let mut ps = MDataPermissionSet::new();
for access in permissions {
ps = match *access {
Permission::Read => ps.allow(MDataAction::Read),
Permission::Insert => ps.allow(MDataAction::Insert),
Permission::Update => ps.allow(MDataAction::Update),
Permission::Delete => ps.allow(MDataAction::Delete),
Permission::ManagePermissions => ps.allow(MDataAction::ManagePermissions),
};
}
ps
}
pub unsafe fn containers_from_repr_c(
raw: *const FfiContainerPermissions,
len: usize,
) -> Result<HashMap<String, ContainerPermissions>, IpcError> {
slice::from_raw_parts(raw, len)
.iter()
.map(|raw| {
Ok((
String::clone_from_repr_c(raw.cont_name)?,
container_perms_from_repr_c(raw.access)?,
))
})
.collect()
}
pub fn permission_set_into_repr_c(perms: MDataPermissionSet) -> FfiPermissionSet {
FfiPermissionSet {
read: perms.is_allowed(MDataAction::Read),
insert: perms.is_allowed(MDataAction::Insert),
update: perms.is_allowed(MDataAction::Update),
delete: perms.is_allowed(MDataAction::Delete),
manage_permissions: perms.is_allowed(MDataAction::ManagePermissions),
}
}
pub fn permission_set_clone_from_repr_c(
perms: FfiPermissionSet,
) -> Result<MDataPermissionSet, IpcError> {
let mut pm = MDataPermissionSet::new();
if perms.read && !perms.insert && !perms.update && !perms.delete && !perms.manage_permissions {
return Err(IpcError::from("Can't convert only the read permission"));
}
if perms.read {
pm = pm.allow(MDataAction::Read);
}
if perms.insert {
pm = pm.allow(MDataAction::Insert);
}
if perms.update {
pm = pm.allow(MDataAction::Update);
}
if perms.delete {
pm = pm.allow(MDataAction::Delete);
}
if perms.manage_permissions {
pm = pm.allow(MDataAction::ManagePermissions);
}
Ok(pm)
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
pub struct AppExchangeInfo {
pub id: String,
pub scope: Option<String>,
pub name: String,
pub vendor: String,
}
impl AppExchangeInfo {
pub fn into_repr_c(self) -> Result<FfiAppExchangeInfo, IpcError> {
let Self {
id,
scope,
name,
vendor,
} = self;
Ok(FfiAppExchangeInfo {
id: CString::new(id).map_err(StringError::from)?.into_raw(),
scope: if let Some(scope) = scope {
CString::new(scope).map_err(StringError::from)?.into_raw()
} else {
ptr::null()
},
name: CString::new(name).map_err(StringError::from)?.into_raw(),
vendor: CString::new(vendor).map_err(StringError::from)?.into_raw(),
})
}
}
impl ReprC for AppExchangeInfo {
type C = *const FfiAppExchangeInfo;
type Error = IpcError;
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let FfiAppExchangeInfo {
id,
scope,
name,
vendor,
} = *repr_c;
Ok(Self {
id: String::clone_from_repr_c(id).map_err(StringError::from)?,
scope: if scope.is_null() {
None
} else {
Some(String::clone_from_repr_c(scope).map_err(StringError::from)?)
},
name: String::clone_from_repr_c(name).map_err(StringError::from)?,
vendor: String::clone_from_repr_c(vendor).map_err(StringError::from)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::btree_set;
use crate::ffi::ipc::req::PermissionSet as FfiPermissionSet;
use ffi_utils::ReprC;
use safe_nd::MDataAction;
use std::collections::HashMap;
use std::ffi::CStr;
use unwrap::unwrap;
#[test]
fn container_permissions() {
let mut cp = HashMap::new();
let _ = cp.insert("foobar".to_string(), btree_set![Permission::Insert]);
let ffi_cp = unwrap!(containers_into_vec(cp));
assert_eq!(ffi_cp.len(), 1);
let cp = unsafe { unwrap!(containers_from_repr_c(ffi_cp.as_ptr(), 1)) };
assert!(cp.contains_key("foobar"));
assert_eq!(unwrap!(cp.get("foobar")), &btree_set![Permission::Insert]);
}
#[test]
fn empty_container_permissions() {
let mut cp = HashMap::new();
let _ = cp.insert("foobar".to_string(), Default::default());
let ffi_cp = unwrap!(containers_into_vec(cp));
assert_eq!(ffi_cp.len(), 1);
let cp = unsafe { containers_from_repr_c(ffi_cp.as_ptr(), 1) };
assert!(cp.is_err());
}
#[test]
fn permissions_set_conversion() {
let ps = FfiPermissionSet {
read: true,
insert: false,
update: false,
delete: false,
manage_permissions: false,
};
let res = permission_set_clone_from_repr_c(ps);
assert!(res.is_err());
let ps = FfiPermissionSet {
read: true,
insert: false,
update: true,
delete: true,
manage_permissions: false,
};
let res = unwrap!(permission_set_clone_from_repr_c(ps));
assert!(res.is_allowed(MDataAction::Update));
assert!(res.is_allowed(MDataAction::Delete));
assert!(!res.is_allowed(MDataAction::Insert));
assert!(!res.is_allowed(MDataAction::ManagePermissions));
}
#[test]
fn app_exchange_info() {
let a = AppExchangeInfo {
id: "myid".to_string(),
scope: Some("hi".to_string()),
name: "bubi".to_string(),
vendor: "hey girl".to_string(),
};
let ffi_a = unwrap!(a.into_repr_c());
unsafe {
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.id).to_str()), "myid");
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.scope).to_str()), "hi");
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.name).to_str()), "bubi");
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.vendor).to_str()), "hey girl");
}
let mut a = unsafe { unwrap!(AppExchangeInfo::clone_from_repr_c(&ffi_a)) };
assert_eq!(a.id, "myid");
assert_eq!(a.scope, Some("hi".to_string()));
assert_eq!(a.name, "bubi");
assert_eq!(a.vendor, "hey girl");
a.scope = None;
let ffi_a = unwrap!(a.into_repr_c());
unsafe {
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.id).to_str()), "myid");
assert!(ffi_a.scope.is_null());
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.name).to_str()), "bubi");
assert_eq!(unwrap!(CStr::from_ptr(ffi_a.vendor).to_str()), "hey girl");
}
}
#[test]
fn auth_request() {
let app = AppExchangeInfo {
id: "1".to_string(),
scope: Some("2".to_string()),
name: "3".to_string(),
vendor: "4".to_string(),
};
let a = AuthReq {
app,
app_container: false,
app_permissions: Default::default(),
containers: HashMap::new(),
};
let ffi = unwrap!(a.into_repr_c());
assert_eq!(ffi.app_container, false);
assert_eq!(ffi.containers_len, 0);
let a = unsafe { unwrap!(AuthReq::clone_from_repr_c(&ffi)) };
assert_eq!(a.app.id, "1");
assert_eq!(a.app.scope, Some("2".to_string()));
assert_eq!(a.app.name, "3");
assert_eq!(a.app.vendor, "4");
assert_eq!(a.app_container, false);
assert_eq!(a.containers.len(), 0);
}
#[test]
fn containers_req() {
let app = AppExchangeInfo {
id: "1".to_string(),
scope: Some("2".to_string()),
name: "3".to_string(),
vendor: "4".to_string(),
};
let a = ContainersReq {
app,
containers: HashMap::new(),
};
let ffi = unwrap!(a.into_repr_c());
assert_eq!(ffi.containers_len, 0);
let a = unsafe { unwrap!(ContainersReq::clone_from_repr_c(&ffi)) };
assert_eq!(a.app.id, "1");
assert_eq!(a.app.scope, Some("2".to_string()));
assert_eq!(a.app.name, "3");
assert_eq!(a.app.vendor, "4");
assert_eq!(a.containers.len(), 0);
}
}