Skip to content

Instantly share code, notes, and snippets.

@mbrubeck
Forked from Dowwie/main.py
Last active January 18, 2017 20:46
Show Gist options
  • Save mbrubeck/100d4c99ec45bb6e34f9a4a66ada9212 to your computer and use it in GitHub Desktop.
Save mbrubeck/100d4c99ec45bb6e34f9a4a66ada9212 to your computer and use it in GitHub Desktop.
Experimenting with refactoring permission authorization with a Rust implementation (thus far, a Rust integration is slower)
# works for python 2.7
import ctypes
import timeit
rustLib = "./libauthz.so"
assigned_perms = ["domain1:action3:target1", "domain1:action1,action2", "domain1:action4:target2"]
required_perm = "domain1:action4:target3"
lib = ctypes.cdll.LoadLibrary(rustLib)
def testRust():
perm_array = (ctypes.c_char_p * len(assigned_perms))(*assigned_perms)
result = lib.is_permitted_from_str(ctypes.c_char_p(required_perm), perm_array, len(assigned_perms))
if __name__=="__main__":
rusty_test = timeit.repeat("testRust()", "from __main__ import testRust", number=100000, repeat=1)
print 'rusty_test: ', min(rusty_test)
bench: libauthz.so
python2 main.py
libauthz.so: rusty_authz.rs Makefile
rustc -C opt-level=3 -C lto rusty_authz.rs --crate-type cdylib -o libauthz.so
.PHONY: bench
#![feature(libc)]
extern crate libc;
use std::slice;
use std::ffi::CStr;
use std::str;
use libc::{size_t, c_char};
use std::collections::HashSet;
#[derive(Debug)]
struct Permission<'a> {
domain: &'a str,
actions: HashSet<&'a str>,
targets: HashSet<&'a str>
}
impl<'a> Permission<'a> {
fn new(wildcard_perm: &str) -> Permission {
let mut perm = Permission {
domain: "",
actions: HashSet::new(),
targets: HashSet::new()};
perm.init_parts(wildcard_perm);
perm
}
fn split_string_by_comma(str: &str) -> HashSet<&str> {
str.split(',').map(str::trim).collect()
}
fn init_parts(&mut self, wildcard_perm: &'a str) {
let mut iter = wildcard_perm.split(':').map(str::trim);
self.domain = match iter.next() {
Some(s) => s,
None => "*"
};
if let Some(s) = iter.next() {
self.actions = Permission::split_string_by_comma(s);
}
if let Some(s) = iter.next() {
self.targets = Permission::split_string_by_comma(s);
}
}
fn implies_from_perm(&self, permission: &Permission) -> bool {
if self.domain != permission.domain || self.domain == "*" {
return false;
}
if !&self.actions.is_superset(&permission.actions) || self.actions.contains("*"){
return false;
}
self.targets.is_superset(&permission.targets) || self.targets.contains("*")
}
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 perms = unsafe {slice::from_raw_parts(assigned_perms, assigned_perms_len as usize)};
let required_permission = unsafe {CStr::from_ptr(required_perm).to_str().unwrap()};
let assigned_permissions = perms.iter()
.map(|&p| unsafe { CStr::from_ptr(p) }) // iterator of &CStr
.map(|cs| cs.to_bytes()) // iterator of &[u8]
.map(|bs| str::from_utf8(bs).unwrap()); // iterator of &str
_is_permitted_from_str(required_permission, assigned_permissions)
}
#[inline]
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);
// see: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
for assigned in assigned_perms {
let assigned_permission = Permission::new(&assigned);
if assigned_permission.implies_from_perm(&required_permission){
return true;
}
}
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment