#![cfg(test)]
use crate::{PolicyExpr, PolicyLiteral, ReadPolicy, ReadPredicate, render::render_read_policy_sql};
use cratestack_core::{CoolContext, Value};
fn render(allow: &[ReadPolicy], deny: &[ReadPolicy], ctx: &CoolContext) -> Option<String> {
let mut bind_index = 1usize;
render_read_policy_sql(allow, deny, ctx, &mut bind_index)
}
#[test]
fn empty_allow_list_default_denies_with_sql_false() {
let ctx = CoolContext::anonymous();
let sql = render(&[], &[], &ctx).expect("empty allow should still render a clause");
assert_eq!(sql, "FALSE");
let authenticated = CoolContext::authenticated([]);
let sql = render(&[], &[], &authenticated).expect("empty allow should still render a clause");
assert_eq!(sql, "FALSE");
}
#[test]
fn deny_beats_allow_precedence() {
let ctx = CoolContext::authenticated([]);
let allow = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::AuthNotNull),
}];
let deny = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::AuthNotNull),
}];
let sql = render(&allow, &deny, &ctx).expect("policy should render");
assert_eq!(sql, "NOT (TRUE) AND (TRUE)");
}
#[test]
fn auth_not_null_and_auth_is_null_collapse_to_sql_booleans() {
let authenticated = CoolContext::authenticated([]);
let anonymous = CoolContext::anonymous();
let allow_not_null = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::AuthNotNull),
}];
let allow_is_null = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::AuthIsNull),
}];
assert_eq!(
render(&allow_not_null, &[], &authenticated).unwrap(),
"TRUE"
);
assert_eq!(render(&allow_not_null, &[], &anonymous).unwrap(), "FALSE");
assert_eq!(
render(&allow_is_null, &[], &authenticated).unwrap(),
"FALSE"
);
assert_eq!(render(&allow_is_null, &[], &anonymous).unwrap(), "TRUE");
}
#[test]
fn has_role_and_in_tenant_collapse_to_sql_booleans() {
let admin =
CoolContext::authenticated([("role".to_owned(), Value::String("admin".to_owned()))]);
let member =
CoolContext::authenticated([("role".to_owned(), Value::String("member".to_owned()))]);
let allow_role = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::HasRole { role: "admin" }),
}];
assert_eq!(render(&allow_role, &[], &admin).unwrap(), "TRUE");
assert_eq!(render(&allow_role, &[], &member).unwrap(), "FALSE");
let tenant_a = CoolContext::authenticated([(
"tenant".to_owned(),
Value::Map(std::collections::BTreeMap::from([(
"id".to_owned(),
Value::String("tenant_a".to_owned()),
)])),
)]);
let allow_tenant = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::InTenant {
tenant_id: "tenant_a",
}),
}];
assert_eq!(render(&allow_tenant, &[], &tenant_a).unwrap(), "TRUE");
assert_eq!(render(&allow_tenant, &[], &member).unwrap(), "FALSE");
}
#[test]
fn auth_field_eq_and_ne_literal_collapse_to_sql_booleans() {
let banned =
CoolContext::authenticated([("status".to_owned(), Value::String("banned".to_owned()))]);
let active =
CoolContext::authenticated([("status".to_owned(), Value::String("active".to_owned()))]);
let allow_eq = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::AuthFieldEqLiteral {
auth_field: "status",
value: PolicyLiteral::String("banned"),
}),
}];
let allow_ne = [ReadPolicy {
expr: PolicyExpr::Predicate(ReadPredicate::AuthFieldNeLiteral {
auth_field: "status",
value: PolicyLiteral::String("banned"),
}),
}];
assert_eq!(render(&allow_eq, &[], &banned).unwrap(), "TRUE");
assert_eq!(render(&allow_eq, &[], &active).unwrap(), "FALSE");
assert_eq!(render(&allow_ne, &[], &banned).unwrap(), "FALSE");
assert_eq!(render(&allow_ne, &[], &active).unwrap(), "TRUE");
}