Skip to content

Commit e9d04fe

Browse files
some comments
1 parent 3c6711d commit e9d04fe

File tree

2 files changed

+227
-0
lines changed

2 files changed

+227
-0
lines changed

cedar-policy/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ cool_asserts = "2.0"
6161
criterion = "0.5"
6262
globset = "0.4"
6363

64+
proptest = "1.0.0"
65+
6466
[[bench]]
6567
name = "cedar_benchmarks"
6668
harness = false

cedar-policy/src/tests.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2726,3 +2726,228 @@ mod schema_based_parsing_tests {
27262726
);
27272727
}
27282728
}
2729+
2730+
mod prop_test_policy_set_operations {
2731+
use super::*;
2732+
use proptest::prelude::*;
2733+
use std::collections::hash_map::Entry;
2734+
2735+
/// Production PolicySet along with simplified model of policy set
2736+
/// for Proptesting
2737+
struct PolicySetModel {
2738+
//The production PolicySet implementation
2739+
policy_set: PolicySet,
2740+
2741+
//Used to generate unique names
2742+
max_name_int: i32,
2743+
2744+
//We model the PolicySet state machine in terms of the below:
2745+
//Every name is unique
2746+
static_policy_names: Vec<String>,
2747+
template_names: Vec<String>,
2748+
link_names: Vec<String>,
2749+
2750+
//Every existent template has a (possibly empty) vector of the links to that template
2751+
template_to_link_map: HashMap<String, Vec<String>>,
2752+
2753+
//Every link points to its template
2754+
link_to_template_map: HashMap<String, String>,
2755+
}
2756+
2757+
/// Model of a PolicySet where ops that shouldn't be allowed have no effect
2758+
/// e.g., remove_static with no static policies does nothing
2759+
impl PolicySetModel {
2760+
fn new() -> Self {
2761+
Self {
2762+
policy_set: PolicySet::new(),
2763+
static_policy_names: Vec::new(),
2764+
template_names: Vec::new(),
2765+
link_names: Vec::new(),
2766+
template_to_link_map: HashMap::new(),
2767+
link_to_template_map: HashMap::new(),
2768+
max_name_int: 0,
2769+
}
2770+
}
2771+
2772+
fn get_next_name(&mut self) -> String {
2773+
let i = self.max_name_int;
2774+
self.max_name_int += 1;
2775+
format!("policy{i}")
2776+
}
2777+
2778+
//add_static never fails and bumps `max_name_int`
2779+
fn add_static(&mut self) {
2780+
let policy_name = self.get_next_name();
2781+
self.static_policy_names.push(policy_name.clone());
2782+
let policy_str = "permit(principal, action, resource);";
2783+
let p = Policy::parse(Some(policy_name), policy_str).unwrap();
2784+
self.policy_set
2785+
.add(p)
2786+
.expect("Should be able to add policy with unique name");
2787+
}
2788+
2789+
//add_static never fails and bumps `max_name_int`
2790+
fn add_template(&mut self) {
2791+
let template_name = self.get_next_name();
2792+
self.template_names.push(template_name.clone());
2793+
let template_str = "permit(principal == ?principal, action, resource);";
2794+
let template = Template::parse(Some(template_name.clone()), template_str).unwrap();
2795+
self.policy_set
2796+
.add_template(template)
2797+
.expect("Should be able to add template with unique name");
2798+
self.template_to_link_map.insert(template_name, Vec::new());
2799+
}
2800+
2801+
fn link(&mut self) {
2802+
if self.template_names.len() > 0 {
2803+
let policy_name = self.get_next_name();
2804+
self.link_names.push(policy_name.clone());
2805+
let euid = EntityUid::from_strs("User", "alice");
2806+
let template_name = self.template_names[0].clone(); //TODO: randomize
2807+
let vals = HashMap::from([(SlotId::principal(), euid)]);
2808+
self.policy_set
2809+
.link(
2810+
PolicyId::from_str(&template_name).unwrap(),
2811+
PolicyId::from_str(&policy_name).unwrap(),
2812+
vals,
2813+
)
2814+
.expect("linking should succeed with existent template and unique link name");
2815+
match self.template_to_link_map.entry(template_name.clone()) {
2816+
Entry::Occupied(v) => v.into_mut().push(policy_name.clone()),
2817+
Entry::Vacant(_) => {
2818+
panic!("template to link map should have Vec for existing template")
2819+
}
2820+
};
2821+
assert!(self.link_to_template_map.get(&policy_name).is_none());
2822+
self.link_to_template_map.insert(policy_name, template_name);
2823+
}
2824+
}
2825+
2826+
fn remove_static(&mut self) {
2827+
//TODO: randomize
2828+
if let Some(policy_id) = self.static_policy_names.last() {
2829+
//Remove from PolicySet and `static_policy_names`
2830+
self.policy_set
2831+
.remove_static(PolicyId::from_str(&policy_id).unwrap())
2832+
.expect("Should be able to remove static policy that exists");
2833+
self.static_policy_names.pop();
2834+
}
2835+
}
2836+
2837+
fn get_name_template_with_no_links(&mut self) -> Option<String> {
2838+
//TODO: randomize
2839+
for (template_name, links) in self.template_to_link_map.iter() {
2840+
if links.len() == 0 {
2841+
return Some(template_name.clone());
2842+
}
2843+
}
2844+
None
2845+
}
2846+
2847+
fn remove_template(&mut self) {
2848+
if let Some(template_name) = self.get_name_template_with_no_links() {
2849+
//Assert no link exists
2850+
assert!(!self
2851+
.link_to_template_map
2852+
.iter()
2853+
.any(|(_, v)| v == &template_name));
2854+
2855+
//Remove from `template_to_link_map`, `template_names` and the PolicySet
2856+
self.template_to_link_map
2857+
.remove(&template_name)
2858+
.expect("Template should exist");
2859+
self.policy_set
2860+
.remove_template(PolicyId::from_str(&template_name).unwrap())
2861+
.expect("Template can be removed");
2862+
let idx = self
2863+
.template_names
2864+
.iter()
2865+
.position(|r| r == &template_name)
2866+
.expect("Should find template_name");
2867+
self.template_names.remove(idx);
2868+
}
2869+
}
2870+
2871+
fn unlink(&mut self) {
2872+
//TODO: randomize
2873+
if let Some(policy_id) = self.link_names.last() {
2874+
//Remove from PolicySet, `link_to_template_map` and `template_to_link_map`
2875+
self.policy_set
2876+
.unlink(PolicyId::from_str(&policy_id).unwrap())
2877+
.expect("Should be able to remove link that exists");
2878+
let template_name = self
2879+
.link_to_template_map
2880+
.get(policy_id)
2881+
.expect("Template should exist")
2882+
.clone();
2883+
self.link_to_template_map
2884+
.remove(policy_id)
2885+
.expect("Template should exist");
2886+
match self.template_to_link_map.entry(template_name.clone()) {
2887+
Entry::Occupied(e) => {
2888+
let v = e.into_mut();
2889+
let idx = v
2890+
.iter()
2891+
.position(|r| r == policy_id)
2892+
.expect("Should find index for link");
2893+
v.remove(idx);
2894+
}
2895+
Entry::Vacant(_) => {
2896+
panic!("template to link map should have Vec for existing template")
2897+
}
2898+
};
2899+
//Remove from `link_names`
2900+
self.link_names.pop();
2901+
}
2902+
}
2903+
}
2904+
2905+
fn string_to_policy_set_ops(s: &str) {
2906+
let mut my_policy_set = PolicySetModel::new();
2907+
enum PolicySetOp {
2908+
Add,
2909+
RemoveStatic,
2910+
AddTemplate,
2911+
RemoveTemplate,
2912+
Link,
2913+
Unlink,
2914+
}
2915+
use PolicySetOp::*;
2916+
let n_to_op_map: HashMap<u32, PolicySetOp> = HashMap::from([
2917+
(0, Add),
2918+
(1, RemoveStatic),
2919+
(2, AddTemplate),
2920+
(3, RemoveTemplate),
2921+
(4, Link),
2922+
(5, Unlink),
2923+
]);
2924+
2925+
for c in s.chars() {
2926+
let n = c.to_digit(10);
2927+
match n {
2928+
Some(n) => {
2929+
if n > 5 {
2930+
panic!("Testing harness sending numbers greater than 5");
2931+
}
2932+
let op = n_to_op_map.get(&n).unwrap();
2933+
match op {
2934+
Add => my_policy_set.add_static(),
2935+
RemoveStatic => my_policy_set.remove_static(),
2936+
AddTemplate => my_policy_set.add_template(),
2937+
RemoveTemplate => my_policy_set.remove_template(),
2938+
Link => my_policy_set.link(),
2939+
Unlink => my_policy_set.unlink(),
2940+
};
2941+
}
2942+
None => continue,
2943+
}
2944+
}
2945+
}
2946+
2947+
proptest! {
2948+
#[test]
2949+
fn doesnt_crash(s in "[0-5]{32}") {
2950+
string_to_policy_set_ops(&s);
2951+
}
2952+
}
2953+
}

0 commit comments

Comments
 (0)