Skip to content

Commit c554807

Browse files
RFC 62 implementation (#1327)
Signed-off-by: Shaobo He <[email protected]>
1 parent 466ffec commit c554807

File tree

14 files changed

+965
-24
lines changed

14 files changed

+965
-24
lines changed

cedar-policy-core/src/ast/expr.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,14 @@ impl<T> Expr<T> {
18611861
}
18621862
}
18631863
}
1864+
1865+
pub(crate) fn is_true(&self) -> bool {
1866+
matches!(self.expr_kind(), ExprKind::Lit(Literal::Bool(true)))
1867+
}
1868+
1869+
pub(crate) fn is_false(&self) -> bool {
1870+
matches!(self.expr_kind(), ExprKind::Lit(Literal::Bool(false)))
1871+
}
18641872
}
18651873

18661874
/// AST variables

cedar-policy-core/src/ast/name.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ impl Name {
523523
self.0.basename().clone().try_into().unwrap()
524524
}
525525

526-
/// Test if a [`Name`] is an [`Id`]
526+
/// Test if a [`Name`] is an [`UnreservedId`]
527527
pub fn is_unqualified(&self) -> bool {
528528
self.0.is_unqualified()
529529
}

cedar-policy-core/src/est.rs

+71
Original file line numberDiff line numberDiff line change
@@ -4485,6 +4485,77 @@ mod test {
44854485
);
44864486
}
44874487
}
4488+
4489+
#[test]
4490+
fn extended_has() {
4491+
let policy_text = r#"
4492+
permit(principal, action, resource) when
4493+
{ principal has a.b.c };"#;
4494+
let cst = parser::text_to_cst::parse_policy(policy_text).unwrap();
4495+
let est: Policy = cst.node.unwrap().try_into().unwrap();
4496+
assert_eq!(
4497+
est,
4498+
serde_json::from_value(json!({
4499+
"effect": "permit",
4500+
"principal": { "op": "All" },
4501+
"action": { "op": "All" },
4502+
"resource": { "op": "All" },
4503+
"conditions": [
4504+
{
4505+
"kind": "when",
4506+
"body": {
4507+
"&&": {
4508+
"left": {
4509+
"&&": {
4510+
"left": {
4511+
"has": {
4512+
"left": {
4513+
"Var": "principal",
4514+
},
4515+
"attr": "a"
4516+
}
4517+
},
4518+
"right": {
4519+
"has": {
4520+
"left": {
4521+
".": {
4522+
"left": {
4523+
"Var": "principal",
4524+
},
4525+
"attr": "a",
4526+
},
4527+
},
4528+
"attr": "b"
4529+
}
4530+
},
4531+
}
4532+
},
4533+
"right": {
4534+
"has": {
4535+
"left": {
4536+
".": {
4537+
"left": {
4538+
".": {
4539+
"left": {
4540+
"Var": "principal",
4541+
},
4542+
"attr": "a"
4543+
}
4544+
},
4545+
"attr": "b",
4546+
}
4547+
},
4548+
"attr": "c",
4549+
}
4550+
},
4551+
},
4552+
},
4553+
}
4554+
]
4555+
}))
4556+
.unwrap()
4557+
);
4558+
}
44884559
}
44894560

44904561
#[cfg(test)]

cedar-policy-core/src/est/expr.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::parser::util::flatten_tuple_2;
2929
use crate::parser::{Loc, Node};
3030
use either::Either;
3131
use itertools::Itertools;
32+
use nonempty::nonempty;
3233
use serde::{de::Visitor, Deserialize, Serialize};
3334
use serde_with::serde_as;
3435
use smol_str::{SmolStr, ToSmolStr};
@@ -1170,11 +1171,23 @@ impl TryFrom<&Node<Option<cst::Relation>>> for Expr {
11701171
Ok(expr)
11711172
}
11721173
cst::Relation::Has { target, field } => {
1173-
let target_expr = target.try_into()?;
1174-
field
1175-
.to_expr_or_special()?
1176-
.into_valid_attr()
1177-
.map(|attr| Expr::has_attr(target_expr, attr))
1174+
let target_expr: Expr = target.try_into()?;
1175+
let attrs = match field.to_has_rhs()? {
1176+
Either::Left(attr) => nonempty![attr],
1177+
Either::Right(ids) => ids.map(|id| id.to_smolstr()),
1178+
};
1179+
let (first, rest) = attrs.split_first();
1180+
let has_expr = Expr::has_attr(target_expr.clone(), first.clone());
1181+
let get_expr = Expr::get_attr(target_expr, first.clone());
1182+
Ok(rest
1183+
.iter()
1184+
.fold((has_expr, get_expr), |(has_expr, get_expr), attr| {
1185+
(
1186+
Expr::and(has_expr, Expr::has_attr(get_expr.clone(), attr.to_owned())),
1187+
Expr::get_attr(get_expr, attr.to_owned()),
1188+
)
1189+
})
1190+
.0)
11781191
}
11791192
cst::Relation::Like { target, pattern } => {
11801193
let target_expr = target.try_into()?;

cedar-policy-core/src/evaluator.rs

+69-1
Original file line numberDiff line numberDiff line change
@@ -5525,7 +5525,7 @@ pub mod test {
55255525
}
55265526

55275527
#[test]
5528-
fn parital_if_noerrors() {
5528+
fn partial_if_noerrors() {
55295529
let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
55305530
let cons = Expr::val(1);
55315531
let alt = Expr::val(2);
@@ -6144,4 +6144,72 @@ pub mod test {
61446144
);
61456145
assert_matches!(eval.partial_eval_expr(&e), Err(_));
61466146
}
6147+
6148+
#[test]
6149+
fn interpret_extended_has() {
6150+
let es = Entities::new();
6151+
let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6152+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6153+
{a: {b: {c: 1}}} has a.b.c
6154+
"#).unwrap()), Ok(v) => {
6155+
assert_eq!(v, Value::from(true));
6156+
});
6157+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6158+
{a: {b: {c: 1}}} has a.b
6159+
"#).unwrap()), Ok(v) => {
6160+
assert_eq!(v, Value::from(true));
6161+
});
6162+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6163+
{a: {b: {c: 1}}} has a
6164+
"#).unwrap()), Ok(v) => {
6165+
assert_eq!(v, Value::from(true));
6166+
});
6167+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6168+
{a: {b: {c: 1}}} has b.c
6169+
"#).unwrap()), Ok(v) => {
6170+
assert_eq!(v, Value::from(false));
6171+
});
6172+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6173+
{a: {b: {c: 1}}} has c
6174+
"#).unwrap()), Ok(v) => {
6175+
assert_eq!(v, Value::from(false));
6176+
});
6177+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6178+
{a: {b: {c: 1}}} has d
6179+
"#).unwrap()), Ok(v) => {
6180+
assert_eq!(v, Value::from(false));
6181+
});
6182+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6183+
{a: {b: {c: 1}}} has "🚫"
6184+
"#).unwrap()), Ok(v) => {
6185+
assert_eq!(v, Value::from(false));
6186+
});
6187+
6188+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6189+
{a: {b: {c: 1}}} has a.b.c && {a: {b: {c: 1}}}.a.b.c == 1
6190+
"#).unwrap()), Ok(v) => {
6191+
assert_eq!(v, Value::from(true));
6192+
});
6193+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6194+
{a: {b: {c: 1}}} has a.b && {a: {b: {c: 1}}}.a.b == {c: 1}
6195+
"#).unwrap()), Ok(v) => {
6196+
assert_eq!(v, Value::from(true));
6197+
});
6198+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6199+
{a: {b: {c: 1}}} has a && {a: {b: {c: 1}}}.a == {b: {c: 1}}
6200+
"#).unwrap()), Ok(v) => {
6201+
assert_eq!(v, Value::from(true));
6202+
});
6203+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6204+
{a: {b: {d: 1}}} has a.b.c && {a: {b: {d: 1}}}.a.b.c == 1
6205+
"#).unwrap()), Ok(v) => {
6206+
assert_eq!(v, Value::from(false));
6207+
});
6208+
6209+
assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6210+
{a: {b: {c: 1}}} has a.b && {a: {b: {c: 1}}}.a.b.d == 1
6211+
"#).unwrap()), Err(EvaluationError::RecordAttrDoesNotExist(err)) => {
6212+
assert_eq!(err.attr, "d");
6213+
});
6214+
}
61476215
}

0 commit comments

Comments
 (0)