Skip to main content

biscuit_auth/token/builder/
block.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 */
5use super::{
6    constrained_rule, date, fact, pred, rule, string, var, Binary, Block, Check, CheckKind,
7    Convert, Expression, Fact, Op, Rule, Scope, Term,
8};
9use crate::builder_ext::BuilderExt;
10use crate::crypto::PublicKey;
11use crate::datalog::{get_schema_version, SymbolTable};
12use crate::error;
13use biscuit_parser::parser::parse_block_source;
14
15use std::time::SystemTime;
16use std::{collections::HashMap, convert::TryInto, fmt};
17
18/// creates a Block content to append to an existing token
19#[derive(Clone, Debug, Default)]
20pub struct BlockBuilder {
21    pub facts: Vec<Fact>,
22    pub rules: Vec<Rule>,
23    pub checks: Vec<Check>,
24    pub scopes: Vec<Scope>,
25    pub context: Option<String>,
26}
27
28impl BlockBuilder {
29    pub fn new() -> BlockBuilder {
30        BlockBuilder::default()
31    }
32
33    pub fn merge(mut self, mut other: BlockBuilder) -> Self {
34        self.facts.append(&mut other.facts);
35        self.rules.append(&mut other.rules);
36        self.checks.append(&mut other.checks);
37
38        if let Some(c) = other.context {
39            self.context = Some(c);
40        }
41        self
42    }
43
44    pub fn fact<F: TryInto<Fact>>(mut self, fact: F) -> Result<Self, error::Token>
45    where
46        error::Token: From<<F as TryInto<Fact>>::Error>,
47    {
48        let fact = fact.try_into()?;
49        fact.validate()?;
50
51        self.facts.push(fact);
52        Ok(self)
53    }
54
55    pub fn rule<R: TryInto<Rule>>(mut self, rule: R) -> Result<Self, error::Token>
56    where
57        error::Token: From<<R as TryInto<Rule>>::Error>,
58    {
59        let rule = rule.try_into()?;
60        rule.validate_parameters()?;
61        self.rules.push(rule);
62        Ok(self)
63    }
64
65    pub fn check<C: TryInto<Check>>(mut self, check: C) -> Result<Self, error::Token>
66    where
67        error::Token: From<<C as TryInto<Check>>::Error>,
68    {
69        let check = check.try_into()?;
70        check.validate_parameters()?;
71        self.checks.push(check);
72        Ok(self)
73    }
74
75    pub fn code<T: AsRef<str>>(self, source: T) -> Result<Self, error::Token> {
76        self.code_with_params(source, HashMap::new(), HashMap::new())
77    }
78
79    /// Add datalog code to the builder, performing parameter subsitution as required
80    /// Unknown parameters are ignored
81    pub fn code_with_params<T: AsRef<str>>(
82        mut self,
83        source: T,
84        params: HashMap<String, Term>,
85        scope_params: HashMap<String, PublicKey>,
86    ) -> Result<Self, error::Token> {
87        let input = source.as_ref();
88
89        let source_result = parse_block_source(input).map_err(|e| {
90            let e2: biscuit_parser::error::LanguageError = e.into();
91            e2
92        })?;
93
94        for (_, fact) in source_result.facts.into_iter() {
95            let mut fact: Fact = fact.into();
96            for (name, value) in &params {
97                let res = match fact.set(name, value) {
98                    Ok(_) => Ok(()),
99                    Err(error::Token::Language(
100                        biscuit_parser::error::LanguageError::Parameters {
101                            missing_parameters, ..
102                        },
103                    )) if missing_parameters.is_empty() => Ok(()),
104                    Err(e) => Err(e),
105                };
106                res?;
107            }
108            fact.validate()?;
109            self.facts.push(fact);
110        }
111
112        for (_, rule) in source_result.rules.into_iter() {
113            let mut rule: Rule = rule.into();
114            for (name, value) in &params {
115                let res = match rule.set(name, value) {
116                    Ok(_) => Ok(()),
117                    Err(error::Token::Language(
118                        biscuit_parser::error::LanguageError::Parameters {
119                            missing_parameters, ..
120                        },
121                    )) if missing_parameters.is_empty() => Ok(()),
122                    Err(e) => Err(e),
123                };
124                res?;
125            }
126            for (name, value) in &scope_params {
127                let res = match rule.set_scope(name, *value) {
128                    Ok(_) => Ok(()),
129                    Err(error::Token::Language(
130                        biscuit_parser::error::LanguageError::Parameters {
131                            missing_parameters, ..
132                        },
133                    )) if missing_parameters.is_empty() => Ok(()),
134                    Err(e) => Err(e),
135                };
136                res?;
137            }
138            rule.validate_parameters()?;
139            self.rules.push(rule);
140        }
141
142        for (_, check) in source_result.checks.into_iter() {
143            let mut check: Check = check.into();
144            for (name, value) in &params {
145                let res = match check.set(name, value) {
146                    Ok(_) => Ok(()),
147                    Err(error::Token::Language(
148                        biscuit_parser::error::LanguageError::Parameters {
149                            missing_parameters, ..
150                        },
151                    )) if missing_parameters.is_empty() => Ok(()),
152                    Err(e) => Err(e),
153                };
154                res?;
155            }
156            for (name, value) in &scope_params {
157                let res = match check.set_scope(name, *value) {
158                    Ok(_) => Ok(()),
159                    Err(error::Token::Language(
160                        biscuit_parser::error::LanguageError::Parameters {
161                            missing_parameters, ..
162                        },
163                    )) if missing_parameters.is_empty() => Ok(()),
164                    Err(e) => Err(e),
165                };
166                res?;
167            }
168            check.validate_parameters()?;
169            self.checks.push(check);
170        }
171
172        Ok(self)
173    }
174
175    pub fn scope(mut self, scope: Scope) -> Self {
176        self.scopes.push(scope);
177        self
178    }
179
180    pub fn context(mut self, context: String) -> Self {
181        self.context = Some(context);
182        self
183    }
184
185    pub(crate) fn build(self, mut symbols: SymbolTable) -> Block {
186        let symbols_start = symbols.current_offset();
187        let public_keys_start = symbols.public_keys.current_offset();
188
189        let mut facts = Vec::new();
190        for fact in self.facts {
191            facts.push(fact.convert(&mut symbols));
192        }
193
194        let mut rules = Vec::new();
195        for rule in &self.rules {
196            rules.push(rule.convert(&mut symbols));
197        }
198
199        let mut checks = Vec::new();
200        for check in &self.checks {
201            checks.push(check.convert(&mut symbols));
202        }
203
204        let mut scopes = Vec::new();
205        for scope in &self.scopes {
206            scopes.push(scope.convert(&mut symbols));
207        }
208
209        let new_syms = symbols.split_at(symbols_start);
210        let public_keys = symbols.public_keys.split_at(public_keys_start);
211        let schema_version = get_schema_version(&facts, &rules, &checks, &scopes);
212
213        Block {
214            symbols: new_syms,
215            facts,
216            rules,
217            checks,
218            context: self.context,
219            version: schema_version.version(),
220            external_key: None,
221            public_keys,
222            scopes,
223        }
224    }
225
226    pub(crate) fn convert_from(
227        block: &Block,
228        symbols: &SymbolTable,
229    ) -> Result<Self, error::Format> {
230        Ok(BlockBuilder {
231            facts: block
232                .facts
233                .iter()
234                .map(|f| Fact::convert_from(f, symbols))
235                .collect::<Result<Vec<Fact>, error::Format>>()?,
236            rules: block
237                .rules
238                .iter()
239                .map(|r| Rule::convert_from(r, symbols))
240                .collect::<Result<Vec<Rule>, error::Format>>()?,
241            checks: block
242                .checks
243                .iter()
244                .map(|c| Check::convert_from(c, symbols))
245                .collect::<Result<Vec<Check>, error::Format>>()?,
246            scopes: block
247                .scopes
248                .iter()
249                .map(|s| Scope::convert_from(s, symbols))
250                .collect::<Result<Vec<Scope>, error::Format>>()?,
251            context: block.context.clone(),
252        })
253    }
254
255    // still used in tests but does not make sense for the public API
256    #[cfg(test)]
257    pub(crate) fn check_right(self, right: &str) -> Result<Self, error::Token> {
258        use crate::builder::{pred, string, var};
259
260        use super::rule;
261
262        let term = string(right);
263        let check = rule(
264            "check_right",
265            &[string(right)],
266            &[
267                pred("resource", &[var("resource_name")]),
268                pred("operation", &[term]),
269                pred("right", &[var("resource_name"), string(right)]),
270            ],
271        );
272
273        self.check(check)
274    }
275}
276
277impl fmt::Display for BlockBuilder {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        for mut fact in self.facts.clone().into_iter() {
280            fact.apply_parameters();
281            writeln!(f, "{};", &fact)?;
282        }
283        for mut rule in self.rules.clone().into_iter() {
284            rule.apply_parameters();
285            writeln!(f, "{};", &rule)?;
286        }
287        for mut check in self.checks.clone().into_iter() {
288            check.apply_parameters();
289            writeln!(f, "{};", &check)?;
290        }
291        Ok(())
292    }
293}
294
295impl BuilderExt for BlockBuilder {
296    fn resource(mut self, name: &str) -> Self {
297        self.facts.push(fact("resource", &[string(name)]));
298        self
299    }
300    fn check_resource(mut self, name: &str) -> Self {
301        self.checks.push(Check {
302            queries: vec![rule(
303                "resource_check",
304                &[string("resource_check")],
305                &[pred("resource", &[string(name)])],
306            )],
307            kind: CheckKind::One,
308        });
309        self
310    }
311    fn operation(mut self, name: &str) -> Self {
312        self.facts.push(fact("operation", &[string(name)]));
313        self
314    }
315    fn check_operation(mut self, name: &str) -> Self {
316        self.checks.push(Check {
317            queries: vec![rule(
318                "operation_check",
319                &[string("operation_check")],
320                &[pred("operation", &[string(name)])],
321            )],
322            kind: CheckKind::One,
323        });
324        self
325    }
326    fn check_resource_prefix(mut self, prefix: &str) -> Self {
327        let check = constrained_rule(
328            "prefix",
329            &[var("resource")],
330            &[pred("resource", &[var("resource")])],
331            &[Expression {
332                ops: vec![
333                    Op::Value(var("resource")),
334                    Op::Value(string(prefix)),
335                    Op::Binary(Binary::Prefix),
336                ],
337            }],
338        );
339
340        self.checks.push(Check {
341            queries: vec![check],
342            kind: CheckKind::One,
343        });
344        self
345    }
346
347    fn check_resource_suffix(mut self, suffix: &str) -> Self {
348        let check = constrained_rule(
349            "suffix",
350            &[var("resource")],
351            &[pred("resource", &[var("resource")])],
352            &[Expression {
353                ops: vec![
354                    Op::Value(var("resource")),
355                    Op::Value(string(suffix)),
356                    Op::Binary(Binary::Suffix),
357                ],
358            }],
359        );
360
361        self.checks.push(Check {
362            queries: vec![check],
363            kind: CheckKind::One,
364        });
365        self
366    }
367
368    fn check_expiration_date(mut self, exp: SystemTime) -> Self {
369        let empty: Vec<Term> = Vec::new();
370        let ops = vec![
371            Op::Value(var("time")),
372            Op::Value(date(&exp)),
373            Op::Binary(Binary::LessOrEqual),
374        ];
375        let check = constrained_rule(
376            "query",
377            &empty,
378            &[pred("time", &[var("time")])],
379            &[Expression { ops }],
380        );
381
382        self.checks.push(Check {
383            queries: vec![check],
384            kind: CheckKind::One,
385        });
386        self
387    }
388}