use crate::cap::Cap;
use crate::permission::*;
use std::sync::atomic::{AtomicBool, Ordering};
static ROOT_CREATED: AtomicBool = AtomicBool::new(false);
#[must_use = "CapRoot is a singleton — discarding it means no capabilities can be granted"]
pub struct CapRoot {
_private: (),
}
pub fn root() -> CapRoot {
if ROOT_CREATED.swap(true, Ordering::SeqCst) {
panic!("capsec::root() called more than once — use try_root() or test_root() instead");
}
CapRoot { _private: () }
}
pub fn try_root() -> Option<CapRoot> {
if ROOT_CREATED.swap(true, Ordering::SeqCst) {
None
} else {
Some(CapRoot { _private: () })
}
}
#[cfg(debug_assertions)]
pub fn test_root() -> CapRoot {
CapRoot { _private: () }
}
impl CapRoot {
pub fn grant<P: Permission>(&self) -> Cap<P> {
Cap::new()
}
pub fn fs_read(&self) -> Cap<FsRead> {
self.grant()
}
pub fn fs_write(&self) -> Cap<FsWrite> {
self.grant()
}
pub fn fs_all(&self) -> Cap<FsAll> {
self.grant()
}
pub fn net_connect(&self) -> Cap<NetConnect> {
self.grant()
}
pub fn net_bind(&self) -> Cap<NetBind> {
self.grant()
}
pub fn net_all(&self) -> Cap<NetAll> {
self.grant()
}
pub fn env_read(&self) -> Cap<EnvRead> {
self.grant()
}
pub fn env_write(&self) -> Cap<EnvWrite> {
self.grant()
}
pub fn spawn(&self) -> Cap<Spawn> {
self.grant()
}
pub fn ambient(&self) -> Cap<Ambient> {
self.grant()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::has::Has;
#[test]
fn test_root_works() {
let root = test_root();
let _cap = root.grant::<FsRead>();
}
#[test]
fn test_root_can_be_called_multiple_times() {
let _r1 = test_root();
let _r2 = test_root();
let _r3 = test_root();
}
#[test]
fn grant_produces_zst() {
let root = test_root();
let cap = root.grant::<FsRead>();
assert_eq!(std::mem::size_of_val(&cap), 0);
}
#[test]
fn convenience_methods_return_correct_types() {
let root = test_root();
fn check_fs_read(_: &impl Has<FsRead>) {}
fn check_fs_write(_: &impl Has<FsWrite>) {}
fn check_fs_all(_: &impl Has<FsAll>) {}
fn check_net_connect(_: &impl Has<NetConnect>) {}
fn check_net_bind(_: &impl Has<NetBind>) {}
fn check_net_all(_: &impl Has<NetAll>) {}
fn check_env_read(_: &impl Has<EnvRead>) {}
fn check_env_write(_: &impl Has<EnvWrite>) {}
fn check_spawn(_: &impl Has<Spawn>) {}
fn check_ambient(_: &impl Has<Ambient>) {}
check_fs_read(&root.fs_read());
check_fs_write(&root.fs_write());
check_fs_all(&root.fs_all());
check_net_connect(&root.net_connect());
check_net_bind(&root.net_bind());
check_net_all(&root.net_all());
check_env_read(&root.env_read());
check_env_write(&root.env_write());
check_spawn(&root.spawn());
check_ambient(&root.ambient());
}
#[test]
fn convenience_equivalent_to_grant() {
let root = test_root();
let _a: Cap<FsRead> = root.fs_read();
let _b: Cap<FsRead> = root.grant();
assert_eq!(std::mem::size_of_val(&_a), std::mem::size_of_val(&_b));
}
}