cedar_policy_core/parser/
fmt.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use super::cst::*;
18use super::node::ASTNode;
19use std::fmt;
20
21/// Helper struct to handle non-existent nodes
22struct View<'a, T>(&'a ASTNode<Option<T>>);
23impl<'a, T: fmt::Display> fmt::Display for View<'a, T> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        if let Some(n) = &self.0.as_inner() {
26            if f.alternate() {
27                write!(f, "{:#}", n)
28            } else {
29                write!(f, "{}", n)
30            }
31        } else {
32            write!(f, "[error]")
33        }
34    }
35}
36
37impl fmt::Display for Policies {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        if f.alternate() {
40            let mut ps = self.0.iter();
41            if let Some(p) = ps.next() {
42                write!(f, "{:#}", View(p))?;
43            }
44            for p in ps {
45                write!(f, "\n\n{:#}", View(p))?;
46            }
47        } else {
48            let mut ps = self.0.iter();
49            if let Some(p) = ps.next() {
50                write!(f, "{}", View(p))?;
51            }
52            for p in ps {
53                write!(f, " {}", View(p))?;
54            }
55        }
56        Ok(())
57    }
58}
59impl fmt::Display for Policy {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        // start with annotations
62        for anno in self.annotations.iter() {
63            if f.alternate() {
64                // each annotation on a new line
65                writeln!(f, "{:#}", View(anno))?;
66            } else {
67                write!(f, "{} ", View(anno))?;
68            }
69        }
70        // main policy body
71        if f.alternate() {
72            write!(f, "{:#}(", View(&self.effect))?;
73            let mut vars = self.variables.iter();
74            // if at least one var ...
75            if let Some(v) = vars.next() {
76                // write out the first one ...
77                write!(f, "\n  {:#}", View(v))?;
78                // ... and write out the others after commas
79                for v in vars {
80                    write!(f, ",\n  {:#}", View(v))?;
81                }
82                // close up the vars
83                write!(f, "\n)")?;
84            } else {
85                // no vars: stay on the same line
86                write!(f, ")")?;
87            }
88            // include conditions on their own lines
89            for c in self.conds.iter() {
90                write!(f, "\n{:#}", View(c))?;
91            }
92            write!(f, ";")?;
93        } else {
94            write!(f, "{}(", View(&self.effect))?;
95            let mut vars = self.variables.iter();
96            // if at least one var ...
97            if let Some(v) = vars.next() {
98                // write out the first one ...
99                write!(f, "{}", View(v))?;
100                // ... and write out the others after commas
101                for v in vars {
102                    write!(f, ",  {}", View(v))?;
103                }
104            }
105            write!(f, ")")?;
106
107            for c in self.conds.iter() {
108                write!(f, " {}", View(c))?;
109            }
110            write!(f, ";")?;
111        }
112        Ok(())
113    }
114}
115
116impl fmt::Display for Annotation {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "@{}({})", View(&self.key), View(&self.value))
119    }
120}
121
122impl fmt::Display for VariableDef {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(f, "{}", View(&self.variable))?;
125        if let Some(name) = &self.name {
126            write!(f, ": {}", View(name))?;
127        }
128        if let Some((op, expr)) = &self.ineq {
129            write!(f, " {} {}", op, View(expr))?;
130        }
131        Ok(())
132    }
133}
134impl fmt::Display for Cond {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        if self.expr.is_none() {
137            write!(f, "{} {{ }}", View(&self.cond))
138        } else if f.alternate() {
139            write!(
140                f,
141                "{} {{\n  {:#}\n}}",
142                View(&self.cond),
143                View(self.expr.as_ref().unwrap())
144            )
145        } else {
146            write!(
147                f,
148                "{} {{{}}}",
149                View(&self.cond),
150                View(self.expr.as_ref().unwrap())
151            )
152        }
153    }
154}
155impl fmt::Display for Expr {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        let expr = &*self.expr;
158        match expr {
159            ExprData::Or(or) => write!(f, "{}", View(or)),
160            ExprData::If(ex1, ex2, ex3) => {
161                write!(f, "if {} then {} else {}", View(ex1), View(ex2), View(ex3))
162            }
163        }
164    }
165}
166impl fmt::Display for Or {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        write!(f, "{}", View(&self.initial))?;
169        for or in self.extended.iter() {
170            write!(f, " || {}", View(or))?;
171        }
172        Ok(())
173    }
174}
175impl fmt::Display for And {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        write!(f, "{}", View(&self.initial))?;
178        for and in self.extended.iter() {
179            write!(f, " && {}", View(and))?;
180        }
181        Ok(())
182    }
183}
184impl fmt::Display for Relation {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            Relation::Common { initial, extended } => {
188                write!(f, "{}", View(initial))?;
189                for (op, add) in extended.iter() {
190                    write!(f, " {} {}", op, View(add))?;
191                }
192            }
193            Relation::Has { target, field } => {
194                write!(f, "{} has {}", View(target), View(field))?;
195            }
196            Relation::Like { target, pattern } => {
197                write!(f, "{} like {}", View(target), View(pattern))?;
198            }
199        }
200        Ok(())
201    }
202}
203impl fmt::Display for RelOp {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        match self {
206            RelOp::Less => write!(f, "<"),
207            RelOp::LessEq => write!(f, "<="),
208            RelOp::GreaterEq => write!(f, ">="),
209            RelOp::Greater => write!(f, ">"),
210            RelOp::NotEq => write!(f, "!="),
211            RelOp::Eq => write!(f, "=="),
212            RelOp::In => write!(f, "in"),
213        }
214    }
215}
216impl fmt::Display for AddOp {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        match self {
219            AddOp::Plus => write!(f, "+"),
220            AddOp::Minus => write!(f, "-"),
221        }
222    }
223}
224impl fmt::Display for MultOp {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        match self {
227            MultOp::Times => write!(f, "*"),
228            MultOp::Divide => write!(f, "/"),
229            MultOp::Mod => write!(f, "%"),
230        }
231    }
232}
233impl fmt::Display for NegOp {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        match self {
236            NegOp::Bang(cnt) => {
237                for _ in 0..*cnt {
238                    write!(f, "!")?;
239                }
240            }
241            // represents too many, current parser accepts a max of 4
242            NegOp::OverBang => write!(f, "!!!!!!!!!!")?,
243            NegOp::Dash(cnt) => {
244                for _ in 0..*cnt {
245                    write!(f, "-")?;
246                }
247            }
248            // represents too many, current parser accepts a max of 4
249            NegOp::OverDash => write!(f, "----------")?,
250        }
251        Ok(())
252    }
253}
254impl fmt::Display for Add {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        write!(f, "{}", View(&self.initial))?;
257        for (op, mult) in self.extended.iter() {
258            write!(f, " {} {}", op, View(mult))?;
259        }
260        Ok(())
261    }
262}
263impl fmt::Display for Mult {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        write!(f, "{}", View(&self.initial))?;
266        for (op, un) in self.extended.iter() {
267            write!(f, " {} {}", op, View(un))?;
268        }
269        Ok(())
270    }
271}
272impl fmt::Display for Unary {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        if let Some(op) = &self.op {
275            write!(f, "{}{}", op, View(&self.item))
276        } else {
277            write!(f, "{}", View(&self.item))
278        }
279    }
280}
281impl fmt::Display for Member {
282    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283        write!(f, "{}", View(&self.item))?;
284        for m in self.access.iter() {
285            write!(f, "{}", View(m))?;
286        }
287        Ok(())
288    }
289}
290impl fmt::Display for MemAccess {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        match self {
293            MemAccess::Field(id) => write!(f, ".{}", View(id))?,
294            MemAccess::Call(exprs) => {
295                write!(f, "(")?;
296                let mut es = exprs.iter();
297                if let Some(ex) = es.next() {
298                    write!(f, "{}", View(ex))?;
299                }
300                for e in es {
301                    write!(f, ", {}", View(e))?;
302                }
303                write!(f, ")")?;
304            }
305            MemAccess::Index(e) => write!(f, "[{}]", View(e))?,
306        }
307        Ok(())
308    }
309}
310impl fmt::Display for Primary {
311    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312        match self {
313            Primary::Literal(lit) => write!(f, "{}", View(lit)),
314            Primary::Ref(rf) => write!(f, "{}", View(rf)),
315            Primary::Name(nm) => write!(f, "{}", View(nm)),
316            Primary::Expr(expr) => write!(f, "({})", View(expr)),
317            Primary::EList(exs) => {
318                write!(f, "[")?;
319                let mut es = exs.iter();
320                if let Some(ex) = es.next() {
321                    write!(f, "{}", View(ex))?;
322                }
323                for e in es {
324                    write!(f, ", {}", View(e))?;
325                }
326                write!(f, "]")
327            }
328            Primary::RInits(mis) => {
329                write!(f, "{{")?;
330                let mut ms = mis.iter();
331                if let Some(i) = ms.next() {
332                    write!(f, "{}", View(i))?;
333                }
334                for i in ms {
335                    write!(f, ", {}", View(i))?;
336                }
337                write!(f, "}}")
338            }
339            Primary::Slot(s) => write!(f, "{}", View(s)),
340        }
341    }
342}
343impl fmt::Display for Name {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        for n in self.path.iter() {
346            write!(f, "{}::", View(n))?;
347        }
348        write!(f, "{}", View(&self.name))?;
349        Ok(())
350    }
351}
352impl fmt::Display for Ref {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        match self {
355            Ref::Uid { path, eid } => {
356                write!(f, "{}::{}", View(path), View(eid))?;
357            }
358            Ref::Ref { path, rinits } => {
359                write!(f, "{}::{{", View(path))?;
360                let mut ris = rinits.iter();
361                if let Some(r) = ris.next() {
362                    write!(f, "{}", View(r))?;
363                }
364                for r in ris {
365                    write!(f, ", {}", View(r))?;
366                }
367                write!(f, "}}")?;
368            }
369        }
370        Ok(())
371    }
372}
373impl fmt::Display for RefInit {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        write!(f, "{}: {}", View(&self.0), View(&self.1))
376    }
377}
378impl fmt::Display for RecInit {
379    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380        write!(f, "{}: {}", View(&self.0), View(&self.1))
381    }
382}
383impl fmt::Display for Ident {
384    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385        match self {
386            Ident::Principal => write!(f, "principal"),
387            Ident::Action => write!(f, "action"),
388            Ident::Resource => write!(f, "resource"),
389            Ident::Context => write!(f, "context"),
390            Ident::True => write!(f, "true"),
391            Ident::False => write!(f, "false"),
392            Ident::Permit => write!(f, "permit"),
393            Ident::Forbid => write!(f, "forbid"),
394            Ident::When => write!(f, "when"),
395            Ident::Unless => write!(f, "unless"),
396            Ident::In => write!(f, "in"),
397            Ident::Has => write!(f, "has"),
398            Ident::Like => write!(f, "like"),
399            Ident::If => write!(f, "if"),
400            Ident::Then => write!(f, "then"),
401            Ident::Else => write!(f, "else"),
402            Ident::Ident(s) => write!(f, "{}", s),
403            Ident::Invalid(s) => write!(f, "{}", s),
404        }
405    }
406}
407impl fmt::Display for Literal {
408    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409        match self {
410            Literal::True => write!(f, "true"),
411            Literal::False => write!(f, "false"),
412            Literal::Num(n) => write!(f, "{}", n),
413            Literal::Str(s) => write!(f, "{}", View(s)),
414        }
415    }
416}
417impl fmt::Display for Str {
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        match self {
420            Str::String(s) | Str::Invalid(s) => {
421                write!(f, "\"{}\"", s)
422            }
423        }
424    }
425}
426
427impl std::fmt::Display for Slot {
428    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429        let src = match self {
430            Slot::Principal => "principal",
431            Slot::Resource => "resource",
432        };
433        write!(f, "?{src}")
434    }
435}
436
437#[cfg(test)]
438mod test {
439    use crate::parser::*;
440
441    // Currently, hese tests supplement the ones in the main test
442    // directory, rather than testing everything themselves
443
444    #[test]
445    fn idempotent1() {
446        // Note: the context field in the head is no longer supported and
447        // will produce an error during CST -> AST conversion. But it is
448        // still correctly parsed & displayed by the CST code.
449        let cstnode1 = text_to_cst::parse_policies(
450            r#"
451
452        permit(principal,action,resource,context)
453        when {
454            -3 != !!2
455        };
456
457        "#,
458        )
459        .expect("parse fail");
460        let cst1 = cstnode1.as_inner().expect("no data");
461        let revert = format!("{}", cst1);
462        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
463        let cst2 = cstnode2.as_inner().expect("no data");
464        println!("{:#}", cst2);
465        assert!(cst1 == cst2);
466    }
467    #[test]
468    fn idempotent2() {
469        let cstnode1 = text_to_cst::parse_policies(
470            r#"
471
472        permit(principal,action,resource,context)
473        when {
474            context.contains(3,"four",five(6,7))
475        };
476
477        "#,
478        )
479        .expect("parse fail");
480        let cst1 = cstnode1.as_inner().expect("no data");
481        let revert = format!("{}", cst1);
482        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
483        let cst2 = cstnode2.as_inner().expect("no data");
484        assert!(cst1 == cst2);
485    }
486    #[test]
487    fn idempotent3() {
488        let cstnode1 = text_to_cst::parse_policies(
489            r#"
490
491        permit(principal,action,resource,context)
492        when {
493            context == {3: 14, "true": false || true }
494        };
495
496        "#,
497        )
498        .expect("parse fail");
499        let cst1 = cstnode1.as_inner().expect("no data");
500        let revert = format!("{}", cst1);
501        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
502        let cst2 = cstnode2.as_inner().expect("no data");
503        assert!(cst1 == cst2);
504    }
505    #[test]
506    fn idempotent4() {
507        let cstnode1 = text_to_cst::parse_policies(
508            r#"
509
510        permit(principal,action,resource,context)
511        when {
512            contains() ||
513            containsAll() ||
514            containsAny() ||
515            "sometext" like "some*" ||
516            Random::naming::of::foo()
517        };
518
519        "#,
520        )
521        .expect("parse fail");
522        let cst1 = cstnode1.as_inner().expect("no data");
523        let revert = format!("{}", cst1);
524        println!("{:#}", cst1);
525        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
526        let cst2 = cstnode2.as_inner().expect("no data");
527        assert!(cst1 == cst2);
528    }
529
530    #[test]
531    fn idempotent5() {
532        let cstnode1 = text_to_cst::parse_policies(
533            r#"
534
535        permit(principal,action,resource,context)
536        when {
537            principle == Group::{uid:"ajn34-3qg3-g5"}
538        };
539
540        "#,
541        )
542        .expect("parse fail");
543        let cst1 = cstnode1.as_inner().expect("no data");
544        let revert = format!("{}", cst1);
545        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
546        let cst2 = cstnode2.as_inner().expect("no data");
547        assert!(cst1 == cst2);
548    }
549}