biscuit_auth/
parser.rs

1/*
2 * Copyright (c) 2019 Geoffroy Couprie <contact@geoffroycouprie.com> and Contributors to the Eclipse Foundation.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5//! Datalog text format parsing
6//!
7//! all of the parsers are usable with [`std::convert::TryFrom`] so they can be used
8//! as follows:
9//!
10//! ```rust
11//! use std::convert::TryInto;
12//! use biscuit_auth::builder::Fact;
13//!
14//! let f: Fact = "test(\"data\")".try_into().expect("parse error");
15//! ```
16//!
17//! All of the methods in [BiscuitBuilder](`crate::token::builder::BiscuitBuilder`)
18//! and [BlockBuilder](`crate::token::builder::BlockBuilder`) can take strings
19//! as arguments too
20
21pub 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 biscuit_parser::parser::Expr;
265        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        //panic!();
366    }
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}