1pub use biscuit_parser::parser::*;
22
23#[cfg(test)]
24mod tests {
25 use crate::{
26 builder::{CheckKind, Convert},
27 datalog,
28 token::builder,
29 };
30 use biscuit_parser::parser::*;
31 use nom::error::ErrorKind;
32
33 #[derive(Debug, PartialEq)]
34 enum Expr {
35 Value(builder::Term),
36 Unary(builder::Op, Box<Expr>),
37 Binary(builder::Op, Box<Expr>, Box<Expr>),
38 Closure(Vec<String>, Box<Expr>),
39 }
40
41 impl Expr {
42 pub fn opcodes(self) -> Vec<builder::Op> {
43 let mut v = Vec::new();
44 self.into_opcodes(&mut v);
45 v
46 }
47
48 fn into_opcodes(self, v: &mut Vec<builder::Op>) {
49 match self {
50 Expr::Value(t) => v.push(builder::Op::Value(t)),
51 Expr::Unary(op, expr) => {
52 expr.into_opcodes(v);
53 v.push(op);
54 }
55 Expr::Binary(op, left, right) => {
56 left.into_opcodes(v);
57 right.into_opcodes(v);
58 v.push(op);
59 }
60 Expr::Closure(params, body) => {
61 let mut ops = vec![];
62 body.into_opcodes(&mut ops);
63 v.push(builder::Op::Closure(params, ops));
64 }
65 }
66 }
67 }
68
69 impl From<biscuit_parser::parser::Expr> for Expr {
70 fn from(e: biscuit_parser::parser::Expr) -> Self {
71 match e {
72 biscuit_parser::parser::Expr::Value(v) => Expr::Value(v.into()),
73 biscuit_parser::parser::Expr::Unary(op, expr) => {
74 Expr::Unary(op.into(), Box::new((*expr).into()))
75 }
76 biscuit_parser::parser::Expr::Binary(op, expr1, expr2) => Expr::Binary(
77 op.into(),
78 Box::new((*expr1).into()),
79 Box::new((*expr2).into()),
80 ),
81 biscuit_parser::parser::Expr::Closure(params, body) => {
82 Expr::Closure(params, Box::new((*body).into()))
83 }
84 }
85 }
86 }
87
88 #[test]
89 fn rule() {
90 assert_eq!(
91 biscuit_parser::parser::rule(
92 "right($0, \"read\") <- resource( $0), operation(\"read\")"
93 )
94 .map(|(i, o)| (i, o.into())),
95 Ok((
96 "",
97 builder::rule(
98 "right",
99 &[builder::variable("0"), builder::string("read"),],
100 &[
101 builder::pred("resource", &[builder::variable("0")]),
102 builder::pred("operation", &[builder::string("read")]),
103 ]
104 )
105 ))
106 );
107 }
108
109 #[test]
110 fn constrained_rule() {
111 use builder::{date, var, Binary, Expression, Op};
112 use std::time::{Duration, SystemTime};
113
114 assert_eq!(
115 biscuit_parser::parser::rule("valid_date(\"file1\") <- time($0 ), resource(\"file1\"), $0 <= 2019-12-04T09:46:41+00:00").map(|(i, o)| (i, o.into())),
116 Ok((
117 "",
118 builder::constrained_rule(
119 "valid_date",
120 &[
121 builder::string("file1"),
122 ],
123 &[
124 builder::pred("time", &[builder::variable("0")]),
125 builder::pred("resource", &[builder::string("file1")]),
126 ],
127 &[Expression {
128 ops: vec![
129 Op::Value(var("0")),
130 Op::Value(date(&(SystemTime::UNIX_EPOCH + Duration::from_secs(1575452801)))),
131 Op::Binary(Binary::LessOrEqual),
132 ]
133 }],
134 )
135 ))
136 );
137 }
138
139 #[test]
140 fn constrained_rule_ordering() {
141 use builder::{date, var, Binary, Expression, Op};
142 use std::time::{Duration, SystemTime};
143
144 assert_eq!(
145 biscuit_parser::parser::rule("valid_date(\"file1\") <- time( $0 ), $0 <= 2019-12-04T09:46:41+00:00, resource(\"file1\")").map(|(i, o)| (i, o.into())),
146 Ok((
147 "",
148 builder::constrained_rule(
149 "valid_date",
150 &[
151 builder::string("file1"),
152 ],
153 &[
154 builder::pred("time", &[builder::variable("0")]),
155 builder::pred("resource", &[builder::string("file1")]),
156 ],
157 &[Expression {
158 ops: vec![
159 Op::Value(var("0")),
160 Op::Value(date(&(SystemTime::UNIX_EPOCH + Duration::from_secs(1575452801)))),
161 Op::Binary(Binary::LessOrEqual),
162 ]
163 }],
164 )
165 ))
166 );
167 }
168
169 #[test]
170 fn rule_with_free_head_variables() {
171 assert_eq!(
172 biscuit_parser::parser::rule("right($0, $test) <- resource($0), operation(\"read\")"),
173 Err( nom::Err::Failure(Error {
174 input: "right($0, $test) <- resource($0), operation(\"read\")",
175 code: ErrorKind::Satisfy,
176 message: Some("the rule contains variables that are not bound by predicates in the rule's body: $test".to_string()),
177 }))
178 );
179 }
180
181 #[test]
182 fn rule_with_free_expression_variables() {
183 assert_eq!(
184 biscuit_parser::parser::rule("right($0) <- resource($0), operation(\"read\"), $test"),
185 Err( nom::Err::Failure(Error {
186 input: "right($0) <- resource($0), operation(\"read\"), $test",
187 code: ErrorKind::Satisfy,
188 message: Some("the rule contains variables that are not bound by predicates in the rule's body: $test".to_string()),
189 }))
190 );
191 }
192
193 #[test]
194 fn check() {
195 let empty: &[builder::Term] = &[];
196 assert_eq!(
197 biscuit_parser::parser::check(
198 "check if resource( $0), operation(\"read\") or admin(\"authority\")"
199 )
200 .map(|(i, o)| (i, o.into())),
201 Ok((
202 "",
203 builder::Check {
204 queries: vec![
205 builder::rule(
206 "query",
207 empty,
208 &[
209 builder::pred("resource", &[builder::variable("0")]),
210 builder::pred("operation", &[builder::string("read")]),
211 ]
212 ),
213 builder::rule(
214 "query",
215 empty,
216 &[builder::pred("admin", &[builder::string("authority")]),]
217 ),
218 ],
219 kind: CheckKind::One,
220 }
221 ))
222 );
223 }
224
225 #[test]
226 fn invalid_check() {
227 assert_eq!(
228 biscuit_parser::parser::check(
229 "check if resource($0) and operation(\"read\") or admin(\"authority\")"
230 ),
231 Err( nom::Err::Error(Error {
232 input: "and",
233 code: ErrorKind::Eof,
234 message: Some("expected either the next term after ',' or the next check variant after 'or', but got 'and'".to_string()),
235 }))
236 );
237
238 assert_eq!(
239 biscuit_parser::parser::check(
240 "check if resource(\"{}\"), operation(\"write\")) or operation(\"read\")"
241 ),
242 Err(nom::Err::Error(Error {
243 input: ")",
244 code: ErrorKind::Eof,
245 message: Some("unexpected parens".to_string()),
246 }))
247 );
248
249 assert_eq!(
250 biscuit_parser::parser::check(
251 "check if resource(\"{}\") && operation(\"write\")) || operation(\"read\")"
252 ),
253 Err( nom::Err::Error(Error {
254 input: "&&",
255 code: ErrorKind::Eof,
256 message: Some("expected either the next term after ',' or the next check variant after 'or', but got '&&'".to_string()),
257 }))
258 );
259 }
260
261 #[test]
262 fn expression() {
263 use crate::datalog::SymbolTable;
264 use builder::{date, int, string, var, Binary, Op, Term};
266 use std::time::{Duration, SystemTime};
267
268 let mut syms = SymbolTable::new();
269
270 let input = " -1 ";
271 println!("parsing: {}", input);
272 let res = biscuit_parser::parser::expr(input).map(|(i, o)| (i, o.into()));
273 assert_eq!(res, Ok((" ", Expr::Value(Term::Integer(-1)))));
274
275 let ops = res.unwrap().1.opcodes();
276 println!("ops: {:#?}", ops);
277 let e = builder::Expression { ops }.convert(&mut syms);
278 println!("print: {}", e.print(&syms).unwrap());
279
280 let input = " $0 <= 2019-12-04T09:46:41+00:00";
281 println!("parsing: {}", input);
282 let res = biscuit_parser::parser::expr(input).map(|(i, o)| (i, o.into()));
283 assert_eq!(
284 res,
285 Ok((
286 "",
287 Expr::Binary(
288 Op::Binary(Binary::LessOrEqual),
289 Box::new(Expr::Value(var("0"))),
290 Box::new(Expr::Value(date(
291 &(SystemTime::UNIX_EPOCH + Duration::from_secs(1575452801))
292 )))
293 )
294 ))
295 );
296
297 let ops = res.unwrap().1.opcodes();
298 println!("ops: {:#?}", ops);
299 let e = builder::Expression { ops }.convert(&mut syms);
300 println!("print: {}", e.print(&syms).unwrap());
301
302 let input = " 1 < $test + 2 ";
303 println!("parsing: {}", input);
304 let res: Result<(&str, Expr), _> =
305 biscuit_parser::parser::expr(input).map(|(i, o)| (i, o.into()));
306 assert_eq!(
307 res,
308 Ok((
309 " ",
310 Expr::Binary(
311 Op::Binary(Binary::LessThan),
312 Box::new(Expr::Value(int(1))),
313 Box::new(Expr::Binary(
314 Op::Binary(Binary::Add),
315 Box::new(Expr::Value(var("test"))),
316 Box::new(Expr::Value(int(2))),
317 ))
318 )
319 ))
320 );
321
322 let ops = res.unwrap().1.opcodes();
323 println!("ops: {:#?}", ops);
324 let e = builder::Expression { ops }.convert(&mut syms);
325 println!("print: {}", e.print(&syms).unwrap());
326
327 let input = " 2 < $test && $var2.starts_with(\"test\") && true ";
328 println!("parsing: {}", input);
329 let res = biscuit_parser::parser::expr(input).map(|(i, o)| (i, o.into()));
330 assert_eq!(
331 res,
332 Ok((
333 " ",
334 Expr::Binary(
335 Op::Binary(Binary::LazyAnd),
336 Box::new(Expr::Binary(
337 Op::Binary(Binary::LazyAnd),
338 Box::new(Expr::Binary(
339 Op::Binary(Binary::LessThan),
340 Box::new(Expr::Value(int(2))),
341 Box::new(Expr::Value(var("test"))),
342 )),
343 Box::new(Expr::Closure(
344 vec![],
345 Box::new(Expr::Binary(
346 Op::Binary(Binary::Prefix),
347 Box::new(Expr::Value(var("var2"))),
348 Box::new(Expr::Value(string("test")))
349 ),)
350 ))
351 )),
352 Box::new(Expr::Closure(
353 vec![],
354 Box::new(Expr::Value(Term::Bool(true)))
355 )),
356 ),
357 ))
358 );
359
360 let ops = res.unwrap().1.opcodes();
361 println!("ops: {:#?}", ops);
362 let e = builder::Expression { ops }.convert(&mut syms);
363 println!("print: {}", e.print(&syms).unwrap());
364
365 }
367
368 #[test]
369 fn parens() {
370 use crate::datalog::{SymbolTable, TemporarySymbolTable};
371 use builder::{int, Binary, Op, Unary};
372 use std::collections::HashMap;
373
374 let mut syms = SymbolTable::new();
375
376 let input = " 1 + 2 * 3 ";
377 println!("parsing: {}", input);
378 let (_, res): (_, Expr) = biscuit_parser::parser::expr(input)
379 .map(|(i, o)| (i, o.into()))
380 .unwrap();
381
382 let ops = res.opcodes();
383 println!("ops: {:#?}", ops);
384 let e = builder::Expression { ops: ops.clone() }.convert(&mut syms);
385
386 let printed = e.print(&syms).unwrap();
387 println!("print: {}", e.print(&syms).unwrap());
388 let h = HashMap::new();
389 let result = e
390 .evaluate(
391 &h,
392 &mut TemporarySymbolTable::new(&syms),
393 &Default::default(),
394 )
395 .unwrap();
396 println!("evaluates to: {:?}", result);
397
398 assert_eq!(
399 ops,
400 vec![
401 Op::Value(int(1)),
402 Op::Value(int(2)),
403 Op::Value(int(3)),
404 Op::Binary(Binary::Mul),
405 Op::Binary(Binary::Add),
406 ]
407 );
408 assert_eq!(&printed, "1 + 2 * 3");
409 assert_eq!(result, datalog::Term::Integer(7));
410
411 let input = " (1 + 2) * 3 ";
412 println!("parsing: {}", input);
413 let (_, res): (_, Expr) = biscuit_parser::parser::expr(input)
414 .map(|(i, o)| (i, o.into()))
415 .unwrap();
416
417 let ops = res.opcodes();
418 println!("ops: {:#?}", ops);
419 let e = builder::Expression { ops: ops.clone() }.convert(&mut syms);
420
421 let printed = e.print(&syms).unwrap();
422 println!("print: {}", e.print(&syms).unwrap());
423 let h = HashMap::new();
424 let result = e
425 .evaluate(
426 &h,
427 &mut TemporarySymbolTable::new(&syms),
428 &Default::default(),
429 )
430 .unwrap();
431 println!("evaluates to: {:?}", result);
432
433 assert_eq!(
434 ops,
435 vec![
436 Op::Value(int(1)),
437 Op::Value(int(2)),
438 Op::Binary(Binary::Add),
439 Op::Unary(Unary::Parens),
440 Op::Value(int(3)),
441 Op::Binary(Binary::Mul),
442 ]
443 );
444 assert_eq!(&printed, "(1 + 2) * 3");
445 assert_eq!(result, datalog::Term::Integer(9));
446 }
447
448 #[test]
449 fn source_file() {
450 use builder::{
451 boolean, constrained_rule, fact, int, pred, rule, string, var, Binary, Check,
452 Expression, Op, Policy, PolicyKind,
453 };
454 use std::time::{Duration, SystemTime};
455
456 let input = r#"
457 fact("string");
458 fact2(1234);
459
460 rule_head($var0) <- fact($var0, $var1), 1 < 2;
461
462 // line comment
463 check if 1 == 2;
464
465 allow if rule_head("string");
466
467 /*
468 other comment
469 */
470 check if
471 fact(5678)
472 or fact(1234), "test".starts_with("abc");
473
474 check if 2021-01-01T00:00:00Z <= 2021-01-01T00:00:00Z;
475
476 deny if true;
477 "#;
478
479 let res = biscuit_parser::parser::parse_source(input);
480 println!("parse_source res:\n{:#?}", res);
481
482 let empty_terms: &[builder::Term] = &[];
483 let empty_preds: &[builder::Predicate] = &[];
484
485 let expected_facts = vec![
486 fact("fact", &[string("string")]),
487 fact("fact2", &[int(1234)]),
488 ];
489
490 let expected_rules = vec![constrained_rule(
491 "rule_head",
492 &[var("var0")],
493 &[pred("fact", &[var("var0"), var("var1")])],
494 &[Expression {
495 ops: vec![
496 Op::Value(int(1)),
497 Op::Value(int(2)),
498 Op::Binary(Binary::LessThan),
499 ],
500 }],
501 )];
502
503 let expected_checks = vec![
504 Check {
505 queries: vec![constrained_rule(
506 "query",
507 empty_terms,
508 empty_preds,
509 &[Expression {
510 ops: vec![
511 Op::Value(int(1)),
512 Op::Value(int(2)),
513 Op::Binary(Binary::HeterogeneousEqual),
514 ],
515 }],
516 )],
517 kind: CheckKind::One,
518 },
519 Check {
520 queries: vec![
521 rule("query", empty_terms, &[pred("fact", &[int(5678)])]),
522 constrained_rule(
523 "query",
524 empty_terms,
525 &[pred("fact", &[int(1234)])],
526 &[Expression {
527 ops: vec![
528 Op::Value(string("test")),
529 Op::Value(string("abc")),
530 Op::Binary(Binary::Prefix),
531 ],
532 }],
533 ),
534 ],
535 kind: CheckKind::One,
536 },
537 Check {
538 queries: vec![constrained_rule(
539 "query",
540 empty_terms,
541 empty_preds,
542 &[Expression {
543 ops: vec![
544 Op::Value(builder::date(
545 &(SystemTime::UNIX_EPOCH + Duration::from_secs(1609459200)),
546 )),
547 Op::Value(builder::date(
548 &(SystemTime::UNIX_EPOCH + Duration::from_secs(1609459200)),
549 )),
550 Op::Binary(Binary::LessOrEqual),
551 ],
552 }],
553 )],
554 kind: CheckKind::One,
555 },
556 ];
557
558 let expected_policies = vec![
559 Policy {
560 kind: PolicyKind::Allow,
561 queries: vec![rule(
562 "query",
563 empty_terms,
564 &[pred("rule_head", &[string("string")])],
565 )],
566 },
567 Policy {
568 kind: PolicyKind::Deny,
569 queries: vec![constrained_rule(
570 "query",
571 empty_terms,
572 empty_preds,
573 &[Expression {
574 ops: vec![Op::Value(boolean(true))],
575 }],
576 )],
577 },
578 ];
579
580 let mut result = res.unwrap();
581 assert_eq!(
582 result
583 .facts
584 .drain(..)
585 .map(|(_, r)| r.into())
586 .collect::<Vec<builder::Fact>>(),
587 expected_facts
588 );
589 assert_eq!(
590 result
591 .rules
592 .drain(..)
593 .map(|(_, r)| r.into())
594 .collect::<Vec<builder::Rule>>(),
595 expected_rules
596 );
597 assert_eq!(
598 result
599 .checks
600 .drain(..)
601 .map(|(_, r)| r.into())
602 .collect::<Vec<builder::Check>>(),
603 expected_checks
604 );
605 assert_eq!(
606 result
607 .policies
608 .drain(..)
609 .map(|(_, r)| r.into())
610 .collect::<Vec<builder::Policy>>(),
611 expected_policies
612 );
613 }
614
615 #[test]
616 fn block_source_file() {
617 use builder::{
618 constrained_rule, fact, int, pred, rule, string, var, Binary, Check, Expression, Op,
619 };
620 use std::time::{Duration, SystemTime};
621
622 let input = r#"
623 fact("string");
624 fact2(1234);
625
626 rule_head($var0) <- fact($var0, $var1), 1 < 2; // line comment
627 check if 1 == 2; /*
628 other comment
629 */
630 check if
631 fact(5678)
632 or fact(1234), "test".starts_with("abc");
633
634 check if 2021-01-01T00:00:00Z <= 2021-01-01T00:00:00Z;
635 "#;
636
637 let res = biscuit_parser::parser::parse_block_source(input);
638 println!("parse_block_source res:\n{:#?}", res);
639
640 let empty_terms: &[builder::Term] = &[];
641 let empty_preds: &[builder::Predicate] = &[];
642
643 let expected_facts = vec![
644 fact("fact", &[string("string")]),
645 fact("fact2", &[int(1234)]),
646 ];
647
648 let expected_rules = vec![constrained_rule(
649 "rule_head",
650 &[var("var0")],
651 &[pred("fact", &[var("var0"), var("var1")])],
652 &[Expression {
653 ops: vec![
654 Op::Value(int(1)),
655 Op::Value(int(2)),
656 Op::Binary(Binary::LessThan),
657 ],
658 }],
659 )];
660
661 let expected_checks = vec![
662 Check {
663 queries: vec![constrained_rule(
664 "query",
665 empty_terms,
666 empty_preds,
667 &[Expression {
668 ops: vec![
669 Op::Value(int(1)),
670 Op::Value(int(2)),
671 Op::Binary(Binary::HeterogeneousEqual),
672 ],
673 }],
674 )],
675 kind: CheckKind::One,
676 },
677 Check {
678 queries: vec![
679 rule("query", empty_terms, &[pred("fact", &[int(5678)])]),
680 constrained_rule(
681 "query",
682 empty_terms,
683 &[pred("fact", &[int(1234)])],
684 &[Expression {
685 ops: vec![
686 Op::Value(string("test")),
687 Op::Value(string("abc")),
688 Op::Binary(Binary::Prefix),
689 ],
690 }],
691 ),
692 ],
693 kind: CheckKind::One,
694 },
695 Check {
696 queries: vec![constrained_rule(
697 "query",
698 empty_terms,
699 empty_preds,
700 &[Expression {
701 ops: vec![
702 Op::Value(builder::date(
703 &(SystemTime::UNIX_EPOCH + Duration::from_secs(1609459200)),
704 )),
705 Op::Value(builder::date(
706 &(SystemTime::UNIX_EPOCH + Duration::from_secs(1609459200)),
707 )),
708 Op::Binary(Binary::LessOrEqual),
709 ],
710 }],
711 )],
712 kind: CheckKind::One,
713 },
714 ];
715
716 let mut result = res.unwrap();
717 assert_eq!(
718 result
719 .facts
720 .drain(..)
721 .map(|(_, r)| r.into())
722 .collect::<Vec<builder::Fact>>(),
723 expected_facts
724 );
725 assert_eq!(
726 result
727 .rules
728 .drain(..)
729 .map(|(_, r)| r.into())
730 .collect::<Vec<builder::Rule>>(),
731 expected_rules
732 );
733 assert_eq!(
734 result
735 .checks
736 .drain(..)
737 .map(|(_, r)| r.into())
738 .collect::<Vec<builder::Check>>(),
739 expected_checks
740 );
741 }
742}