Skip to content

Instantly share code, notes, and snippets.

@mbrubeck
Last active January 21, 2017 18:51
Show Gist options
  • Save mbrubeck/1574b56153a8b6a2a001b7d09cc789c8 to your computer and use it in GitHub Desktop.
Save mbrubeck/1574b56153a8b6a2a001b7d09cc789c8 to your computer and use it in GitHub Desktop.
A working beta of permission authorization, written in Rust, intended to replace the python implementation in Yosai
#![feature(libc)]
#![feature(test)]
extern crate libc;
extern crate test;
use std::slice;
use std::ffi::CStr;
use std::str;
use libc::{size_t, c_char};
use std::collections::HashSet;
use test::Bencher;
static PART_DELIMETER: &'static str = ":";
static SUBPART_DELIMETER: &'static str = ",";
#[derive(Debug)]
struct Permission<'a> {
domain: Option<&'a str>,
actions: Option<HashSet<&'a str>>,
targets: Option<HashSet<&'a str>>
}
impl<'a> Permission<'a> {
fn new(wildcard_perm: &str) -> Permission {
let mut perm = Permission {
domain: None,
actions: None,
targets: None,
};
perm.init_parts(wildcard_perm);
perm
}
fn part_from_str(s: Option<&str>) -> Option<HashSet<&str>> {
match s {
Some("") | None => None,
Some(s) => {
let mut set = HashSet::new();
for rule in s.split(SUBPART_DELIMETER).map(str::trim) {
if rule == "*" { return None }
set.insert(rule);
}
Some(set)
}
}
}
fn init_parts(&mut self, wildcard_perm: &'a str) {
let mut iter = wildcard_perm.split(PART_DELIMETER).map(str::trim);
self.domain = match iter.next() {
Some("") | Some("*") | None => None,
domain => domain
};
self.actions = Permission::part_from_str(iter.next());
self.targets = Permission::part_from_str(iter.next());
}
fn implies_from_perm(&self, permission: &Permission) -> bool {
match (self.domain, permission.domain) {
(Some(my_domain), Some(other_domain)) if my_domain != other_domain => return false,
_ => {}
}
match (self.actions.as_ref(), permission.actions.as_ref()) {
(Some(my_set), Some(other_set)) if !my_set.is_superset(other_set) => {
return false;
}
(None, Some(_)) => return false,
_ => {}
}
match (self.actions.as_ref(), permission.actions.as_ref()) {
(Some(my_set), Some(other_set)) if !my_set.is_superset(other_set) => {
return false;
}
(None, Some(_)) => return false,
_ => {}
}
return true;
}
fn implies_from_str(&self, wildcard_permission: &str) -> bool {
let permission = Permission::new(wildcard_permission);
self.implies_from_perm(&permission)
}
}
#[no_mangle]
pub extern fn is_permitted_from_str(required_perm: *const c_char, assigned_perms: *const *const c_char, assigned_perms_len: size_t) -> bool{
let ffi_required_permission = unsafe {CStr::from_ptr(required_perm)};
let required_permission = match ffi_required_permission.to_str() {
Ok(s) => s,
Err(e) => return false
};
let perms = unsafe {slice::from_raw_parts(assigned_perms, assigned_perms_len as usize)};
let assigned_permissions = perms.iter()
.map(|&p| unsafe { CStr::from_ptr(p) })
.map(|cs| cs.to_bytes())
.map(|bs| str::from_utf8(bs).unwrap_or_else(|_| ""));
_is_permitted_from_str(required_permission, assigned_permissions)
}
fn _is_permitted_from_str<'a, I>(required_perm: &str, assigned_perms: I) -> bool
where I: IntoIterator<Item=&'a str>
{
let required_permission = Permission::new(&required_perm);
for assigned in assigned_perms {
let assigned_permission = Permission::new(assigned);
if assigned_permission.implies_from_perm(&required_permission){
return true;
}
}
return false;
}
fn test_perm() {
let required_perm = "domain1:action4:target4";
let assigned_perms: Vec<&str> = vec!("domain1:action1", "domain1:action2", "domain1:action4");
let _ = _is_permitted_from_str(&required_perm, assigned_perms);
}
#[bench]
fn test_is_permitted_from_str(b: &mut Bencher) {
b.iter(|| test_perm())
}
fn main() {
let perm1: Permission = Permission::new("domain1:action1");
let perm2: Permission = Permission::new("domain1:action1,action2");
let perm3: Permission = Permission::new("domain1:action1,action2:target1");
let perm4: Permission = Permission::new("domain1:action3,action4:target2,target3");
let perm5: Permission = Permission::new("domain1:action1,action2,action3,action4");
assert_eq!(perm5.implies_from_perm(&perm1), true);
assert_eq!(perm5.implies_from_perm(&perm2), true);
assert_eq!(perm5.implies_from_perm(&perm3), true);
assert_eq!(perm5.implies_from_perm(&perm4), true);
assert_eq!(perm1.implies_from_perm(&perm5), false);
assert_eq!(perm3.implies_from_perm(&perm5), false);
assert_eq!(perm5.implies_from_str("domain1:action1"), true);
assert_eq!(perm5.implies_from_str("domain1:action3, action4:target2, target3"), true);
assert_eq!(perm1.implies_from_str("domain1:action3, action4:target2, target3"), false);
assert_eq!(perm3.implies_from_str("domain1:action3, action4:target2, target3"), false);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment