#![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 ffi::ipc::req::{AppExchangeInfo as FfiAppExchangeInfo,
ContainerPermissions as FfiContainerPermissions,
PermissionSet as FfiPermissionSet};
use ffi_utils::{ReprC, StringError, from_c_str};
use ipc::errors::IpcError;
use routing::{Action, PermissionSet};
use std::{ptr, slice};
use std::collections::{BTreeSet, HashMap};
use std::ffi::{CString, NulError};
#[repr(C)]
#[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) -> PermissionSet
where
Iter: IntoIterator<Item = &'a Permission>,
{
let mut ps = PermissionSet::new();
for access in permissions {
ps = match *access {
Permission::Read => ps,
Permission::Insert => ps.allow(Action::Insert),
Permission::Update => ps.allow(Action::Update),
Permission::Delete => ps.allow(Action::Delete),
Permission::ManagePermissions => ps.allow(Action::ManagePermissions),
};
}
ps
}
#[allow(unsafe_code)]
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((
from_c_str(raw.cont_name)?,
container_perms_from_repr_c(raw.access)?,
))
})
.collect()
}
pub fn permission_set_into_repr_c(perms: PermissionSet) -> FfiPermissionSet {
FfiPermissionSet {
read: true,
insert: perms.is_allowed(Action::Insert).unwrap_or(false),
update: perms.is_allowed(Action::Update).unwrap_or(false),
delete: perms.is_allowed(Action::Delete).unwrap_or(false),
manage_permissions: perms.is_allowed(Action::ManagePermissions).unwrap_or(false),
}
}
pub fn permission_set_clone_from_repr_c(
perms: &FfiPermissionSet,
) -> Result<PermissionSet, IpcError> {
let mut pm = PermissionSet::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.insert {
pm = pm.allow(Action::Insert);
}
if perms.update {
pm = pm.allow(Action::Update);
}
if perms.delete {
pm = pm.allow(Action::Delete);
}
if perms.manage_permissions {
pm = pm.allow(Action::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 AppExchangeInfo {
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(raw: *const FfiAppExchangeInfo) -> Result<Self, IpcError> {
Ok(AppExchangeInfo {
id: from_c_str((*raw).id).map_err(StringError::from)?,
scope: if (*raw).scope.is_null() {
None
} else {
Some(from_c_str((*raw).scope).map_err(StringError::from)?)
},
name: from_c_str((*raw).name).map_err(StringError::from)?,
vendor: from_c_str((*raw).vendor).map_err(StringError::from)?,
})
}
}
#[cfg(test)]
#[allow(unsafe_code)]
mod tests {
use super::*;
use ffi::ipc::req::PermissionSet as FfiPermissionSet;
use ffi_utils::ReprC;
use std::collections::HashMap;
use std::ffi::CStr;
#[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!(unwrap!(res.is_allowed(Action::Update)));
assert!(unwrap!(res.is_allowed(Action::Delete)));
assert!(res.is_allowed(Action::Insert).is_none());
assert!(res.is_allowed(Action::ManagePermissions).is_none());
}
#[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,
app_container: false,
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: 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);
}
}