biscuit_auth/
parser.rs

1//! Datalog text format parsing
2//!
3//! all of the parsers are usable with [`std::convert::TryFrom`] so they can be used
4//! as follows:
5//!
6//! ```rust
7//! use std::convert::TryInto;
8//! use biscuit_auth::builder::Fact;
9//!
10//! let f: Fact = "test(\"data\")".try_into().expect("parse error");
11//! ```
12//!
13//! All of the methods in [BiscuitBuilder](`crate::token::builder::BiscuitBuilder`)
14//! and [BlockBuilder](`crate::token::builder::BlockBuilder`) can take strings
15//! as arguments too
16
17pub 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 biscuit_parser::parser::Expr;
252        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        //panic!();
347    }
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}