Created
January 24, 2017 18:54
-
-
Save mbrubeck/80dcf96d7f47c8e22e3ece12e07e5f26 to your computer and use it in GitHub Desktop.
rusty authz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(libc)] | |
#![feature(test)] | |
extern crate libc; | |
extern crate test; | |
extern crate seahash; | |
use std::slice; | |
use std::ffi::CStr; | |
use std::panic; | |
use std::str; | |
use libc::{size_t, c_char}; | |
use std::collections::HashSet; | |
use std::hash::{BuildHasherDefault, Hash}; | |
use test::Bencher; | |
use seahash::SeaHasher; | |
type SeaHashSet<K> = HashSet<K, BuildHasherDefault<SeaHasher>>; | |
static PART_DELIMETER: &'static str = ":"; | |
static SUBPART_DELIMETER: &'static str = ","; | |
#[derive(Debug)] | |
struct Permission<T: Hash + Eq> { | |
domain: Option<T>, | |
actions: Option<SeaHashSet<T>>, | |
targets: Option<SeaHashSet<T>> | |
} | |
impl<'a> Permission<&'a str> { | |
fn new(wildcard_perm: &str) -> Permission<&str> { | |
let mut perm = Permission { | |
domain: None, | |
actions: None, | |
targets: None, | |
}; | |
perm.init_parts(wildcard_perm); | |
perm | |
} | |
fn part_from_str(s: Option<&str>) -> Option<SeaHashSet<&str>> { | |
match s { | |
Some("") | None => None, | |
Some(s) => { | |
let mut set = SeaHashSet::default(); | |
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_str(&self, wildcard_permission: &str) -> bool { | |
let permission = Permission::new(wildcard_permission); | |
self.implies_from_perm(&permission) | |
} | |
} | |
impl<T> Permission<T> where T: Hash + Eq { | |
fn implies_from_perm(&self, permission: &Permission<T>) -> bool { | |
match (self.domain.as_ref(), permission.domain.as_ref()) { | |
(Some(ref my_domain), Some(ref 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; | |
} | |
} | |
#[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) -> i32 { | |
let res = panic::catch_unwind(|| { | |
let ffi_required_permission = unsafe {CStr::from_ptr(required_perm)}; | |
let required_permission = match ffi_required_permission.to_str() { | |
Ok(s) => s, | |
Err(_) => return -1 | |
}; | |
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) | |
}); | |
match res { | |
Ok(rc) => rc, | |
Err(_) => -1, | |
} | |
} | |
fn _is_permitted_from_str<'a, I>(required_perm: &str, assigned_perms: I) -> i32 | |
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 1; | |
} | |
} | |
return 0; | |
} | |
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<&str> = Permission::new("domain1:action1"); | |
let perm2: Permission<&str> = Permission::new("domain1:action1,action2"); | |
let perm3: Permission<&str> = Permission::new("domain1:action1,action2:target1"); | |
let perm4: Permission<&str> = Permission::new("domain1:action3,action4:target2,target3"); | |
let perm5: Permission<&str> = 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