Instantly share code, notes, and snippets.
Last active
January 21, 2017 18:51
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
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
This file contains 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; | |
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