1pub mod cst;
21mod cst_to_ast;
23pub mod err;
25mod fmt;
27mod node;
29pub use node::{ASTNode, SourceInfo};
30pub mod text_to_cst;
32pub(crate) mod unescape;
34
35use smol_str::SmolStr;
36use std::collections::HashMap;
37
38use crate::ast;
39use crate::est;
40
41pub fn parse_policyset(text: &str) -> Result<ast::PolicySet, Vec<err::ParseError>> {
44 let mut errs = Vec::new();
45 text_to_cst::parse_policies(text)?
46 .to_policyset(&mut errs)
47 .ok_or(errs)
48}
49
50pub fn parse_policyset_to_ests_and_pset(
54 text: &str,
55) -> Result<(HashMap<ast::PolicyID, est::Policy>, ast::PolicySet), err::ParseErrors> {
56 let mut errs = Vec::new();
57 let cst = text_to_cst::parse_policies(text).map_err(err::ParseErrors)?;
58 let pset = cst.to_policyset(&mut errs);
59 if pset.is_none() {
60 return Err(err::ParseErrors(errs));
61 }
62 let ests = cst
63 .with_generated_policyids()
64 .expect("shouldn't be None since parse_policies() didn't return Err")
65 .map(|(id, policy)| match &policy.node {
66 Some(p) => Ok(Some((id, p.clone().try_into()?))),
67 None => Ok(None),
68 })
69 .collect::<Result<Option<HashMap<ast::PolicyID, est::Policy>>, err::ParseErrors>>()?;
70 match (errs.is_empty(), ests, pset) {
71 (true, Some(ests), Some(pset)) => Ok((ests, pset)),
72 (_, _, _) => Err(err::ParseErrors(errs)),
73 }
74}
75
76pub fn parse_policy_template(
80 id: Option<String>,
81 text: &str,
82) -> Result<ast::Template, Vec<err::ParseError>> {
83 let mut errs = Vec::new();
84 let id = match id {
85 Some(id) => ast::PolicyID::from_string(id),
86 None => ast::PolicyID::from_string("policy0"),
87 };
88 let r = text_to_cst::parse_policy(text)?.to_policy_template(id, &mut errs);
89 if errs.is_empty() {
90 r.ok_or(errs).map(ast::Template::from)
91 } else {
92 Err(errs)
93 }
94}
95
96pub fn parse_policy_template_to_est_and_ast(
100 id: Option<String>,
101 text: &str,
102) -> Result<(est::Policy, ast::Template), err::ParseErrors> {
103 let mut errs = Vec::new();
104 let id = match id {
105 Some(id) => ast::PolicyID::from_string(id),
106 None => ast::PolicyID::from_string("policy0"),
107 };
108 let cst = text_to_cst::parse_policy(text).map_err(err::ParseErrors)?;
109 let ast = cst
110 .to_policy_template(id, &mut errs)
111 .ok_or_else(|| err::ParseErrors(errs.clone()))?;
112 let est = cst.node.map(TryInto::try_into).transpose()?;
113 match (errs.is_empty(), est) {
114 (true, Some(est)) => Ok((est, ast)),
115 (_, _) => Err(err::ParseErrors(errs)),
116 }
117}
118
119pub fn parse_policy(
123 id: Option<String>,
124 text: &str,
125) -> Result<ast::StaticPolicy, Vec<err::ParseError>> {
126 let mut errs = Vec::new();
127 let id = match id {
128 Some(id) => ast::PolicyID::from_string(id),
129 None => ast::PolicyID::from_string("policy0"),
130 };
131 let r = text_to_cst::parse_policy(text)?.to_policy(id, &mut errs);
132
133 if errs.is_empty() {
134 r.ok_or(errs)
135 } else {
136 Err(errs)
137 }
138}
139
140pub fn parse_policy_to_est_and_ast(
144 id: Option<String>,
145 text: &str,
146) -> Result<(est::Policy, ast::StaticPolicy), err::ParseErrors> {
147 let mut errs = Vec::new();
148 let id = match id {
149 Some(id) => ast::PolicyID::from_string(id),
150 None => ast::PolicyID::from_string("policy0"),
151 };
152 let cst = text_to_cst::parse_policy(text).map_err(err::ParseErrors)?;
153 let ast = cst
154 .to_policy(id, &mut errs)
155 .ok_or_else(|| err::ParseErrors(errs.clone()))?;
156
157 let est = cst.node.map(TryInto::try_into).transpose()?;
158 match (errs.is_empty(), est) {
159 (true, Some(est)) => Ok((est, ast)),
160 (_, _) => Err(err::ParseErrors(errs)),
161 }
162}
163
164pub fn parse_expr(ptext: &str) -> Result<ast::Expr, Vec<err::ParseError>> {
166 let mut errs = Vec::new();
167 text_to_cst::parse_expr(ptext)?
168 .to_expr(&mut errs)
169 .ok_or(errs)
170}
171
172pub fn parse_restrictedexpr(ptext: &str) -> Result<ast::RestrictedExpr, Vec<err::ParseError>> {
174 parse_expr(ptext)
175 .and_then(|expr| ast::RestrictedExpr::new(expr).map_err(|err| vec![err.into()]))
176}
177
178pub fn parse_euid(euid: &str) -> Result<ast::EntityUID, Vec<err::ParseError>> {
180 let mut errs = Vec::new();
181 text_to_cst::parse_ref(euid)?.to_ref(&mut errs).ok_or(errs)
182}
183
184pub fn parse_name(name: &str) -> Result<ast::Name, Vec<err::ParseError>> {
186 let mut errs = Vec::new();
187 text_to_cst::parse_name(name)?
188 .to_name(&mut errs)
189 .ok_or(errs)
190}
191
192pub fn parse_namespace(name: &str) -> Result<Vec<ast::Id>, Vec<err::ParseError>> {
194 Ok(if name.is_empty() {
198 Vec::new()
199 } else {
200 let name = parse_name(name)?;
201 let once = std::iter::once(name.id);
202 let namespace_path = name.path.as_ref().iter().cloned();
203 namespace_path.chain(once).collect()
204 })
205}
206
207pub fn parse_literal(val: &str) -> Result<ast::Literal, Vec<err::ParseError>> {
209 let mut errs = Vec::new();
210 match text_to_cst::parse_primary(val)?
211 .to_expr(&mut errs)
212 .ok_or(errs)?
213 .into_expr_kind()
214 {
215 ast::ExprKind::Lit(v) => Ok(v),
216 _ => Err(vec![err::ParseError::ToAST(
217 "text is not a literal".to_string(),
218 )]),
219 }
220}
221
222pub fn parse_internal_string(val: &str) -> Result<SmolStr, Vec<err::ParseError>> {
233 let mut errs = Vec::new();
234 text_to_cst::parse_primary(&format!(r#""{val}""#))?
236 .to_string_literal(&mut errs)
237 .ok_or(errs)
238}
239
240pub fn parse_ident(id: &str) -> Result<ast::Id, Vec<err::ParseError>> {
242 let mut errs = Vec::new();
243 text_to_cst::parse_ident(id)?
244 .to_valid_ident(&mut errs)
245 .ok_or(errs)
246}
247
248pub fn parse_request(
250 principal: impl AsRef<str>, action: impl AsRef<str>, resource: impl AsRef<str>, context_json: serde_json::Value, ) -> Result<ast::Request, Vec<err::ParseError>> {
255 let mut errs = vec![];
256 let mut parse_par = |s, name| {
258 parse_euid(s)
259 .map_err(|e| {
260 errs.push(err::ParseError::WithContext {
261 context: format!("trying to parse {}", name),
262 errs: e,
263 })
264 })
265 .ok()
266 };
267
268 let (principal, action, resource) = (
269 parse_par(principal.as_ref(), "principal"),
270 parse_par(action.as_ref(), "action"),
271 parse_par(resource.as_ref(), "resource"),
272 );
273
274 let context = match ast::Context::from_json_value(context_json) {
275 Ok(ctx) => Some(ctx),
276 Err(e) => {
277 errs.push(err::ParseError::ToAST(format!(
278 "failed to parse context JSON: {}",
279 err::ParseErrors(vec![err::ParseError::ToAST(e.to_string())])
280 )));
281 None
282 }
283 };
284 match (principal, action, resource, errs.as_slice()) {
285 (Some(p), Some(a), Some(r), &[]) => Ok(ast::Request {
286 principal: ast::EntityUIDEntry::concrete(p),
287 action: ast::EntityUIDEntry::concrete(a),
288 resource: ast::EntityUIDEntry::concrete(r),
289 context,
290 }),
291 _ => Err(errs),
292 }
293}
294
295#[cfg(test)]
296mod test {
297 use std::collections::HashSet;
298
299 use itertools::Itertools;
300
301 use crate::ast::{test_generators::*, Template};
302
303 use super::*;
304
305 #[test]
306 fn test_template_parsing() {
307 for template in all_templates().map(Template::from) {
308 let id = template.id();
309 let src = format!("{template}");
310 let parsed = parse_policy_template(Some(id.to_string()), &src);
311 match parsed {
312 Ok(p) => {
313 assert_eq!(
314 p.slots().collect::<HashSet<_>>(),
315 template.slots().collect::<HashSet<_>>()
316 );
317 assert_eq!(p.id(), template.id());
318 assert_eq!(p.effect(), template.effect());
319 assert_eq!(p.principal_constraint(), template.principal_constraint());
320 assert_eq!(p.action_constraint(), template.action_constraint());
321 assert_eq!(p.resource_constraint(), template.resource_constraint());
322 assert!(
323 p.non_head_constraints()
324 .eq_shape(template.non_head_constraints()),
325 "{:?} and {:?} should have the same shape.",
326 p.non_head_constraints(),
327 template.non_head_constraints()
328 );
329 }
330 Err(e) => panic!(
331 "Failed to parse {src}, {}",
332 e.into_iter().map(|e| format!("{e}")).join("\n")
333 ),
334 }
335 }
336 }
337
338 #[test]
339 fn test_error_out() {
340 let errors = parse_policyset(
341 r#"
342 permit(principal:p,action:a,resource:r)
343 when{w or if c but not z} // expr error
344 unless{u if c else d or f} // expr error
345 advice{"doit"};
346
347 permit(principality in Group::"jane_friends", // policy error
348 action in [PhotoOp::"view", PhotoOp::"comment"],
349 resource in Album::"jane_trips");
350
351 forbid(principal, action, resource)
352 when { "private" in resource.tags }
353 unless { resource in principal.account };
354 "#,
355 )
356 .expect_err("multiple errors above");
357 println!("{:?}", errors);
358 assert!(errors.len() >= 3);
359 }
360}
361
362#[cfg(test)]
363mod eval_tests {
364 use super::*;
365 use crate::evaluator as eval;
366 use crate::extensions::Extensions;
367
368 #[test]
369 fn interpret_exprs() {
370 let request = eval::test::basic_request();
371 let entities = eval::test::basic_entities();
372 let exts = Extensions::none();
373 let evaluator = eval::Evaluator::new(&request, &entities, &exts).unwrap();
374
375 let expr = parse_expr("false").expect("parse fail");
377 assert_eq!(
378 evaluator
379 .interpret_inline_policy(&expr)
380 .expect("interpret fail"),
381 ast::Value::Lit(ast::Literal::Bool(false))
382 );
383 let expr = parse_expr("true && true").expect("parse fail");
384 assert_eq!(
385 evaluator
386 .interpret_inline_policy(&expr)
387 .expect("interpret fail"),
388 ast::Value::Lit(ast::Literal::Bool(true))
389 );
390 let expr = parse_expr("!true || false && !true").expect("parse fail");
391 assert_eq!(
392 evaluator
393 .interpret_inline_policy(&expr)
394 .expect("interpret fail"),
395 ast::Value::Lit(ast::Literal::Bool(false))
396 );
397 let expr = parse_expr("!!!!true").expect("parse fail");
398 assert_eq!(
399 evaluator
400 .interpret_inline_policy(&expr)
401 .expect("interpret fail"),
402 ast::Value::Lit(ast::Literal::Bool(true))
403 );
404
405 let expr = parse_expr(
406 r#"
407 if false || true != 4 then
408 600
409 else
410 -200
411 "#,
412 )
413 .expect("parse fail");
414 assert_eq!(
415 evaluator
416 .interpret_inline_policy(&expr)
417 .expect("interpret fail"),
418 ast::Value::Lit(ast::Literal::Long(600))
419 );
420 }
421
422 #[test]
423 fn interpret_membership() {
424 let request = eval::test::basic_request();
425 let entities = eval::test::rich_entities();
426 let exts = Extensions::none();
427 let evaluator = eval::Evaluator::new(&request, &entities, &exts).unwrap();
428
429 let expr = parse_expr(
430 r#"
431
432 test_entity_type::"child" in
433 test_entity_type::"unrelated"
434
435 "#,
436 )
437 .expect("parse fail");
438 assert_eq!(
439 evaluator
440 .interpret_inline_policy(&expr)
441 .expect("interpret fail"),
442 ast::Value::Lit(ast::Literal::Bool(false))
443 );
444 let expr = parse_expr(
445 r#"
446
447 test_entity_type::"child" in
448 test_entity_type::"child"
449
450 "#,
451 )
452 .expect("parse fail");
453 assert_eq!(
454 evaluator
455 .interpret_inline_policy(&expr)
456 .expect("interpret fail"),
457 ast::Value::Lit(ast::Literal::Bool(true))
458 );
459 let expr = parse_expr(
460 r#"
461
462 other_type::"other_child" in
463 test_entity_type::"parent"
464
465 "#,
466 )
467 .expect("parse fail");
468 assert_eq!(
469 evaluator
470 .interpret_inline_policy(&expr)
471 .expect("interpret fail"),
472 ast::Value::Lit(ast::Literal::Bool(true))
473 );
474 let expr = parse_expr(
475 r#"
476
477 test_entity_type::"child" in
478 test_entity_type::"grandparent"
479
480 "#,
481 )
482 .expect("parse fail");
483 assert_eq!(
484 evaluator
485 .interpret_inline_policy(&expr)
486 .expect("interpret fail"),
487 ast::Value::Lit(ast::Literal::Bool(true))
488 );
489 }
490
491 #[test]
492 fn interpret_relation() {
493 let request = eval::test::basic_request();
494 let entities = eval::test::basic_entities();
495 let exts = Extensions::none();
496 let evaluator = eval::Evaluator::new(&request, &entities, &exts).unwrap();
497
498 let expr = parse_expr(
499 r#"
500
501 3 < 2 || 2 > 3
502
503 "#,
504 )
505 .expect("parse fail");
506 assert_eq!(
507 evaluator
508 .interpret_inline_policy(&expr)
509 .expect("interpret fail"),
510 ast::Value::Lit(ast::Literal::Bool(false))
511 );
512 let expr = parse_expr(
513 r#"
514
515 7 <= 7 && 4 != 5
516
517 "#,
518 )
519 .expect("parse fail");
520 assert_eq!(
521 evaluator
522 .interpret_inline_policy(&expr)
523 .expect("interpret fail"),
524 ast::Value::Lit(ast::Literal::Bool(true))
525 );
526 }
527}
528
529#[cfg(test)]
530mod parse_tests {
531 use super::*;
532
533 #[test]
534 fn parse_exists() {
535 let result = parse_policyset(
536 r#"
537 permit(principal, action, resource)
538 when{ true };
539 "#,
540 );
541 assert!(!result.expect("parse error").is_empty());
542 }
543
544 #[test]
545 fn test_parse_namespace() -> Result<(), Vec<err::ParseError>> {
546 assert_eq!(Vec::<ast::Id>::new(), parse_namespace("")?);
547 assert_eq!(vec!["Foo".parse::<ast::Id>()?], parse_namespace("Foo")?);
548 assert_eq!(
549 vec!["Foo".parse::<ast::Id>()?, "Bar".parse::<ast::Id>()?],
550 parse_namespace("Foo::Bar")?
551 );
552 assert_eq!(
553 vec![
554 "Foo".parse::<ast::Id>()?,
555 "Bar".parse::<ast::Id>()?,
556 "Baz".parse::<ast::Id>()?
557 ],
558 parse_namespace("Foo::Bar::Baz")?
559 );
560 Ok(())
561 }
562
563 #[test]
564 fn test_parse_string() {
565 assert_eq!(
567 ast::Eid::new(parse_internal_string(r#"a\nblock\nid"#).expect("should parse"))
568 .to_string(),
569 r#"a\nblock\nid"#,
570 );
571 parse_internal_string(r#"oh, no, a '! "#).expect("single quote should be fine");
572 parse_internal_string(r#"oh, no, a "! "#).expect_err("double quote not allowed");
573 parse_internal_string(r#"oh, no, a \"! and a \'! "#).expect("escaped quotes should parse");
574 }
575}