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