Skip to content

Commit

Permalink
WIP:first acl prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
kauncoder committed Jan 30, 2024
1 parent 74e12fd commit e2a59c7
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 150 deletions.
2 changes: 1 addition & 1 deletion rules.json5
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"ke": "demo/example/**",
"action": "Write",
"permission": true
"permission": false
},
{
"sub": {
Expand Down
199 changes: 56 additions & 143 deletions zenoh/src/net/routing/interceptor/authz.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use std::fs::File;
use std::hash::Hash;
use std::io::Read;
use std::{fmt, hash::Hash};

use super::RoutingContext;
use csv::ReaderBuilder;
//use super::RoutingContext;
use fr_trie::key::ValueMerge;
use serde::{Deserialize, Serialize};
use serde_json::{Result, Value};
use zenoh_config::ZenohId;
//use zenoh_protocol::network::NetworkMessage;

use zenoh_protocol::network::NetworkMessage;
use zenoh_result::ZResult;

use fr_trie::glob::acl::{Acl, AclTrie, Permissions};
use fr_trie::glob::acl::Acl;
use fr_trie::glob::GlobMatcher;
use fr_trie::trie::Trie;

use std::{collections::HashMap, error::Error};
use std::collections::HashMap;

#[derive(Clone, Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
pub enum Action {
Expand All @@ -41,10 +41,25 @@ pub struct RequestBuilder {
obj: Option<String>,
action: Option<Action>,
}
#[derive(Clone, Debug)]

type KeTree = AclTrie;
pub enum Permissions {
Deny,
Allow,
}

impl ValueMerge for Permissions {
fn merge(&self, _other: &Self) -> Self {
self.clone()
}

fn merge_mut(&mut self, _other: &Self) {}
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
//type KeTree = AclTrie;
type KeTree = Trie<Acl, Permissions>;

#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct Subject {
id: ZenohId,
attributes: Option<Vec<String>>, //might be mapped to other types eventually
Expand Down Expand Up @@ -127,38 +142,9 @@ impl SubjectBuilder {
}
}

// pub trait ZAuth {
// fn authz_testing(&self, _: String, _: String, _: String) -> ZResult<bool>;
// }

// impl ZAuth for Enforcer {
// fn authz_testing(&self, zid: String, ke: String, act: String) -> ZResult<bool> {
// /*
// (zid, keyexpr, act): these values should be extraced from the authn code.
// has to be atomic, to avoid another process sending the wrong info
// */
// if let Ok(authorized) = self.enforce((zid.clone(), ke.clone(), act.clone())) {
// Ok(authorized)
// } else {
// println!("policy enforcement error");
// Ok(false)
// }
// }
// }

/* replaced with PolicyEnforcer::init() function */

// pub async fn start_authz() -> Result<Enforcer> {
// // get file value
// let mut e = Enforcer::new("keymatch_model.conf", "keymatch_policy.csv").await?;
// e.enable_log(true);
// Ok(e)
// }

//struct that defines each policy (add policy type and ruleset)
//struct that defines a single rule in the access-control policy
#[derive(Serialize, Deserialize, Clone)]
pub struct Rule {
// policy_type: u8, //for l,a,r [access-list, abac, rbac type policy] will be assuming acl for now
sub: Subject,
ke: String,
action: Action,
Expand All @@ -168,9 +154,8 @@ pub struct Rule {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SubAct(Subject, Action);

#[derive(Clone)] //, PartialEq, Eq, Hash)]
pub struct PolicyEnforcer(HashMap<SubAct, KeTree>); //need to add tries here

#[derive(Clone)]
pub struct PolicyEnforcer(HashMap<SubAct, KeTree>);
#[derive(Clone, PartialEq, Eq, Hash)]

pub struct KeTrie {}
Expand All @@ -183,142 +168,89 @@ impl PolicyEnforcer {
creates the policy hashmap with the ke-tries for ke matching
should have polic-type in the mix here...need to verify
*/
//policy should be derived from config/file (hardcoding it for now)
//config for local static policy
// let policy_info = Self::policy_resource_point().unwrap();

//desearlize to vector of rules
// let rule_set: Vec<Rule> = serde_json::from_str(policy_info)?;

let rule_set = Self::policy_resource_point().unwrap();
let rule_set = Self::policy_resource_point("rules.json5").unwrap();
let pe = Self::build_policy_map(rule_set).expect("policy not established");
//also should start the logger here
Ok(pe)
}

pub fn build_policy_map(rule_set: Vec<Rule>) -> ZResult<PolicyEnforcer> {
//let pe: PolicyEnforcer;
let mut policy = PolicyEnforcer(HashMap::new());

//convert vector of rules to a hashmap mapping subact to ketree (WIP)
//convert vector of rules to a hashmap mapping subact to ketrie
/*
policy = subject : [ rule_1,
rule_2,
...
rule_n
]
where rule_i = action_i : (ke_tree_deny, ke_tree_allow) that deny/allow action_i
representaiton of policy list as a hashmap of trees
tried KeTrees, didn't work
using fr-trie for now as a placeholder for key-matching
*/

// let mut policy = Policy(HashMap::new());
//now create a hashmap for ketrees ((sub->action)->ketree)
// let rules: HashMap<String, KeTree>; //u8 is 0 for disallowed and 1 for allowed??
//iterate through the map to get
let mut policy = PolicyEnforcer(HashMap::new());
//create a hashmap for ketries ((sub,action)->ketrie) from the vector of rules
for v in rule_set {
//for each rule
//extract subject and action

/*
for now permission not allowed means it will not be added to the allow trie
*/
// for now permission being false means this ke will not be inserted into the trie of allowed ke's
let perm = v.permission;
if !perm {
continue;
}
let sub = v.sub;
let action = v.action;
let ke = v.ke;
//let perm = v.permission;

//create subact
let subact = SubAct(sub, action);
//match subact in the policy hashmap
#[allow(clippy::map_entry)]
if !policy.0.contains_key(&subact) {
//create new entry for subact + ketree
let mut ketree = KeTree::new();
ketree.insert(Acl::new(&ke), Permissions::READ);
// ketree.insert(ke,1).unwrap(); //1 for allowed??
//ketree.insert(Acl::new(&ke), Permissionssions::READ);
ketree.insert(Acl::new(&ke), Permissions::Allow);
policy.0.insert(subact, ketree);
} else {
let ketree = policy.0.get_mut(&subact).unwrap();
// ketree.insert(ke,1).unwrap(); //1 for allowed??
// let mut ketree = KeTree::new();
// let x = Permissions::READ;
ketree.insert(Acl::new(&ke), Permissions::READ);
// policy.0.insert(subact,ketree);
//ketree.insert(Acl::new(&ke), Permissionssions::READ);
ketree.insert(Acl::new(&ke), Permissions::Allow);
}
}
//return policy;

Ok(policy)
}
pub fn policy_enforcement_point(&self, new_ctx: NewCtx, action: Action) -> ZResult<bool> {
/*
input: msg body
input: new_context and action (sub,act for now but will need attribute values later)
output: allow/deny
function: depending on the msg, builds the subject, builds the request, passes the request to policy_decision_point()
collects result from PDP and then uses that allow/deny output to block or pass the msg to routing table
*/

let ke = new_ctx.ke;
let zid = new_ctx.zid.unwrap();
//build subject here

//build subject
let subject = SubjectBuilder::new().id(zid).build()?;
//build request
let request = RequestBuilder::new()
.sub(subject)
.obj(ke)
.action(action)
.build()?;

//call PDP
let decision = self.policy_decision_point(request)?;
Ok(decision)
}
// pub fn permission_request_builder(
// msg: zenoh_protocol::network::NetworkMessage,
// action: Action,
// ) {

// /*
// input: msg body
// output: (sub,ke,act)
// function: extract relevant info from the incoming msg body
// build the subject [ID, Attributes and Roles]
// then use that to build the request [subject, key-expression, action ]
// return request to PEP
// */
// /*
// PHASE1: just extract the ID (zid?) from the msg; can later add attributes to the list. have a struct with ID and attributes field (both Option)
// */
// }

pub fn policy_decision_point(&self, request: Request) -> ZResult<bool> {
/*
input: (request)
output: true(allow)/false(deny)
function: process the request from PEP against the policy (self)
policy list will(might) be a hashmap of subject:rules_vector (test and discuss)
*/
/*
PHASE1: policy decisions are hardcoded against the policy list; can later update them using a config file.
*/
//representaiton of policy list as a hashmap of trees?
// HashMap<id,Hashmap<action,KeBoxTree>>
/* use KeTrees for mapping R/W values? //need to check this out
tried KeTrees, didn't work
need own algorithm for pattern matching via modified trie-search
function: process the request received from PEP against the policy (self)
policy list is be a hashmap of (subject,action)->ketries (test and discuss)
*/

//extract subject and action from request and create subact [this is our key for hashmap]
//extract subject and action from request and create subact [this will be our key for hashmap]
let subact = SubAct(request.sub, request.action);
let ke = request.obj;
// type policymap =
match self.0.get(&subact) {
Some(ktrie) => {
// check if request ke has a match in ke-trie
// if ke in ke-trie, then Ok(true) else Ok(false)
//let trie = self.0.get.(&subact).clone();
// check if request ke has a match in ke-trie; if ke in ketrie, then Ok(true) else Ok(false)
let result = ktrie.get_merge::<GlobMatcher>(&Acl::new(&ke));
if let Some(value) = result {
if let Some(_value) = result {
return Ok(true);
}
}
Expand All @@ -328,42 +260,23 @@ impl PolicyEnforcer {
Ok(false)
}

pub fn policy_resource_point() -> ZResult<Vec<Rule>> {
pub fn policy_resource_point(file_path: &str) -> ZResult<Vec<Rule>> {
/*
input: config file value along with &self
output: loads the appropriate policy into the memory and returns back self (now with added policy info); might also select AC type (ACL or ABAC)
*/

/*
PHASE1: just have a vector of structs containing these values; later we can load them here from config
input: path to rules.json file
output: loads the appropriate policy into the memory and returns back a vector of rules;
* might also be the point to select AC type (ACL, ABAC etc)??
*/
#[derive(Serialize, Deserialize, Clone)]
struct Rules(Vec<Rule>);

struct Rules(Vec<Rule>); // = Vec::new();
// let mut rdr = ReaderBuilder::new()
// .has_headers(true)
// .from_path("rules.csv")
// .unwrap();
let mut file = File::open("rules.json5").unwrap();
let mut file = File::open(file_path).unwrap();
let mut buff = String::new();
file.read_to_string(&mut buff).unwrap();

let rulevec: Rules = serde_json::from_str(&buff).unwrap();
// for result in rdr.deserialize() {
// if let Ok(rec) = result {
// let record: Rule = rec;
// rule_set.push(record);
// } else {
// bail!("unable to parse json file");
// }
// }

Ok(rulevec.0)
}
}

// fn ketrie_matcher(ke,ketrie){

// }

#[cfg(test)]
Expand Down
6 changes: 0 additions & 6 deletions zenoh/src/net/routing/interceptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,7 @@ impl InterceptorTrait for IngressAclEnforcer {
let new_ctx = NewCtx { ke, zid: Some(zid) }; //how to get the zid here
let decision = e.policy_enforcement_point(new_ctx, act).unwrap();
if !decision {
// println!("Not allowed to Write");
return None;
} else {
// println!("Allowed to Write");
}
}
}
Expand All @@ -248,10 +245,7 @@ impl InterceptorTrait for EgressAclEnforcer {
let new_ctx = NewCtx { ke, zid: self.zid };
let decision = e.policy_enforcement_point(new_ctx, act).unwrap();
if !decision {
// println!("Not allowed to Read");
return None;
} else {
// println!("Allowed to Read");
}
}
}
Expand Down

0 comments on commit e2a59c7

Please sign in to comment.