use std::str::FromStr;
use cedar_policy::PolicySet;
#[derive(Debug)]
pub struct ValidationResult {
pub valid: bool,
pub error: Option<String>,
}
pub fn validate_cedar(policy: &str) -> ValidationResult {
match PolicySet::from_str(policy) {
Ok(_) => ValidationResult {
valid: true,
error: None,
},
Err(e) => ValidationResult {
valid: false,
error: Some(e.to_string()),
},
}
}
pub fn validate_template(template: &str) -> ValidationResult {
let re_quoted = regex::Regex::new(r#"["']\{\{(\w+)\}\}["']"#).unwrap();
let rendered = re_quoted
.replace_all(template, "\"placeholder\"")
.to_string();
let re_set = regex::Regex::new(r"\[\s*\{\{(\w+)\}\}\s*\]").unwrap();
let rendered = re_set
.replace_all(&rendered, "[\"placeholder\"]")
.to_string();
let re_bare = regex::Regex::new(r"\{\{(\w+)\}\}").unwrap();
let rendered = re_bare
.replace_all(&rendered, "placeholder_value")
.to_string();
validate_cedar(&rendered)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_policy() {
let policy = r#"permit (
principal,
action,
resource
)
when {
principal.role == "admin"
};"#;
let res = validate_cedar(policy);
assert!(res.valid, "expected valid, got: {:?}", res.error);
}
#[test]
fn invalid_policy() {
let res = validate_cedar("this is not cedar");
assert!(!res.valid);
assert!(res.error.is_some());
}
#[test]
fn valid_template_quoted_placeholder() {
let tmpl = r#"permit (principal, action, resource)
when {
principal.role == "{{role_value}}"
};"#;
let res = validate_template(tmpl);
assert!(res.valid, "expected valid, got: {:?}", res.error);
}
#[test]
fn valid_template_set_placeholder() {
let tmpl = r#"permit (principal, action, resource)
when {
principal.role in [{{cedar_roles_set}}]
};"#;
let res = validate_template(tmpl);
assert!(res.valid, "expected valid, got: {:?}", res.error);
}
#[test]
fn valid_template_bare_placeholder() {
let tmpl = r#"permit (principal, action, resource)
when {
principal.{{attribute}} == "{{value}}"
};"#;
let res = validate_template(tmpl);
assert!(res.valid, "expected valid, got: {:?}", res.error);
}
}