1use std::{
4 collections::BTreeSet,
5 time::{SystemTime, UNIX_EPOCH},
6};
7
8use super::Block;
10use crate::crypto::PublicKey;
11use crate::datalog::SymbolTable;
12pub use crate::datalog::{
13 Binary as DatalogBinary, Expression as DatalogExpression, Op as DatalogOp,
14 Unary as DatalogUnary,
15};
16use crate::error;
17
18mod algorithm;
19mod authorizer;
20mod biscuit;
21mod block;
22mod check;
23mod expression;
24mod fact;
25mod policy;
26mod predicate;
27mod rule;
28mod scope;
29mod term;
30
31pub use algorithm::*;
32pub use authorizer::*;
33pub use biscuit::*;
34pub use block::*;
35pub use check::*;
36pub use expression::*;
37pub use fact::*;
38pub use policy::*;
39pub use predicate::*;
40pub use rule::*;
41pub use scope::*;
42pub use term::*;
43
44pub trait Convert<T>: Sized {
45 fn convert(&self, symbols: &mut SymbolTable) -> T;
46 fn convert_from(f: &T, symbols: &SymbolTable) -> Result<Self, error::Format>;
47 fn translate(
48 f: &T,
49 from_symbols: &SymbolTable,
50 to_symbols: &mut SymbolTable,
51 ) -> Result<T, error::Format> {
52 Ok(Self::convert_from(f, from_symbols)?.convert(to_symbols))
53 }
54}
55
56pub fn fact<I: AsRef<Term>>(name: &str, terms: &[I]) -> Fact {
58 let pred = pred(name, terms);
59 Fact::new(pred.name, pred.terms)
60}
61
62pub fn pred<I: AsRef<Term>>(name: &str, terms: &[I]) -> Predicate {
64 Predicate {
65 name: name.to_string(),
66 terms: terms.iter().map(|term| term.as_ref().clone()).collect(),
67 }
68}
69
70pub fn rule<T: AsRef<Term>, P: AsRef<Predicate>>(
72 head_name: &str,
73 head_terms: &[T],
74 predicates: &[P],
75) -> Rule {
76 Rule::new(
77 pred(head_name, head_terms),
78 predicates.iter().map(|p| p.as_ref().clone()).collect(),
79 Vec::new(),
80 vec![],
81 )
82}
83
84pub fn constrained_rule<T: AsRef<Term>, P: AsRef<Predicate>, E: AsRef<Expression>>(
86 head_name: &str,
87 head_terms: &[T],
88 predicates: &[P],
89 expressions: &[E],
90) -> Rule {
91 Rule::new(
92 pred(head_name, head_terms),
93 predicates.iter().map(|p| p.as_ref().clone()).collect(),
94 expressions.iter().map(|c| c.as_ref().clone()).collect(),
95 vec![],
96 )
97}
98
99pub fn check<P: AsRef<Predicate>>(predicates: &[P], kind: CheckKind) -> Check {
101 let empty_terms: &[Term] = &[];
102 Check {
103 queries: vec![Rule::new(
104 pred("query", empty_terms),
105 predicates.iter().map(|p| p.as_ref().clone()).collect(),
106 vec![],
107 vec![],
108 )],
109 kind,
110 }
111}
112
113pub fn int(i: i64) -> Term {
115 Term::Integer(i)
116}
117
118pub fn string(s: &str) -> Term {
120 Term::Str(s.to_string())
121}
122
123pub fn date(t: &SystemTime) -> Term {
127 let dur = t.duration_since(UNIX_EPOCH).unwrap();
128 Term::Date(dur.as_secs())
129}
130
131pub fn var(s: &str) -> Term {
133 Term::Variable(s.to_string())
134}
135
136pub fn variable(s: &str) -> Term {
138 Term::Variable(s.to_string())
139}
140
141pub fn bytes(s: &[u8]) -> Term {
143 Term::Bytes(s.to_vec())
144}
145
146pub fn boolean(b: bool) -> Term {
148 Term::Bool(b)
149}
150
151pub fn set(s: BTreeSet<Term>) -> Term {
153 Term::Set(s)
154}
155
156pub fn parameter(p: &str) -> Term {
158 Term::Parameter(p.to_string())
159}
160
161#[cfg(feature = "datalog-macro")]
162pub enum AnyParam {
163 Term(Term),
164 PublicKey(PublicKey),
165}
166
167#[cfg(feature = "datalog-macro")]
168pub trait ToAnyParam {
169 fn to_any_param(&self) -> AnyParam;
170}
171
172#[cfg(feature = "datalog-macro")]
173impl ToAnyParam for PublicKey {
174 fn to_any_param(&self) -> AnyParam {
175 AnyParam::PublicKey(*self)
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use std::{collections::HashMap, convert::TryFrom};
182
183 use super::*;
184
185 #[test]
186 fn set_rule_parameters() {
187 let mut rule = Rule::try_from(
188 "fact($var1, {p2}, {p5}) <- f1($var1, $var3), f2({p2}, $var3, {p4}), $var3.starts_with({p2})",
189 )
190 .unwrap();
191 rule.set("p2", "hello").unwrap();
192 rule.set("p4", 0i64).unwrap();
193 rule.set("p4", 1i64).unwrap();
194
195 let mut term_set = BTreeSet::new();
196 term_set.insert(int(0i64));
197 rule.set("p5", term_set).unwrap();
198
199 let s = rule.to_string();
200 assert_eq!(s, "fact($var1, \"hello\", {0}) <- f1($var1, $var3), f2(\"hello\", $var3, 1), $var3.starts_with(\"hello\")");
201 }
202
203 #[test]
204 fn set_closure_parameters() {
205 let mut rule = Rule::try_from("fact(true) <- false || {p1}").unwrap();
206 rule.set_lenient("p1", true).unwrap();
207 println!("{rule:?}");
208 let s = rule.to_string();
209 assert_eq!(s, "fact(true) <- false || true");
210
211 let mut rule = Rule::try_from("fact(true) <- false || {p1}").unwrap();
212 rule.set("p1", true).unwrap();
213 let s = rule.to_string();
214 assert_eq!(s, "fact(true) <- false || true");
215 }
216
217 #[test]
218 fn set_rule_scope_parameters() {
219 let pubkey = PublicKey::from_bytes(
220 &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db")
221 .unwrap(),
222 Algorithm::Ed25519,
223 )
224 .unwrap();
225 let mut rule = Rule::try_from(
226 "fact($var1, {p2}) <- f1($var1, $var3), f2({p2}, $var3, {p4}), $var3.starts_with({p2}) trusting {pk}",
227 )
228 .unwrap();
229 rule.set("p2", "hello").unwrap();
230 rule.set("p4", 0i64).unwrap();
231 rule.set("p4", 1i64).unwrap();
232 rule.set_scope("pk", pubkey).unwrap();
233
234 let s = rule.to_string();
235 assert_eq!(s, "fact($var1, \"hello\") <- f1($var1, $var3), f2(\"hello\", $var3, 1), $var3.starts_with(\"hello\") trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db");
236 }
237
238 #[test]
239 fn set_code_parameters() {
240 let mut builder = BlockBuilder::new();
241 let mut params = HashMap::new();
242 params.insert("p1".to_string(), "hello".into());
243 params.insert("p2".to_string(), 1i64.into());
244 params.insert("p3".to_string(), true.into());
245 params.insert("p4".to_string(), "this will be ignored".into());
246 let pubkey = PublicKey::from_bytes(
247 &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db")
248 .unwrap(),
249 Algorithm::Ed25519,
250 )
251 .unwrap();
252 let mut scope_params = HashMap::new();
253 scope_params.insert("pk".to_string(), pubkey);
254 builder = builder
255 .code_with_params(
256 r#"fact({p1}, "value");
257 rule($head_var) <- f1($head_var), {p2} > 0 trusting {pk};
258 check if {p3} trusting {pk};
259 "#,
260 params,
261 scope_params,
262 )
263 .unwrap();
264 assert_eq!(
265 format!("{}", &builder),
266 r#"fact("hello", "value");
267rule($head_var) <- f1($head_var), 1 > 0 trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db;
268check if true trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db;
269"#
270 );
271 }
272
273 #[test]
274 fn forbid_unbound_parameters() {
275 let builder = BlockBuilder::new();
276
277 let mut fact = Fact::try_from("fact({p1}, {p4})").unwrap();
278 fact.set("p1", "hello").unwrap();
279 let res = builder.clone().fact(fact);
280 assert_eq!(
281 res.unwrap_err(),
282 error::Token::Language(biscuit_parser::error::LanguageError::Parameters {
283 missing_parameters: vec!["p4".to_string()],
284 unused_parameters: vec![],
285 })
286 );
287 let mut rule = Rule::try_from(
288 "fact($var1, {p2}) <- f1($var1, $var3), f2({p2}, $var3, {p4}), $var3.starts_with({p2})",
289 )
290 .unwrap();
291 rule.set("p2", "hello").unwrap();
292 let res = builder.clone().rule(rule);
293 assert_eq!(
294 res.unwrap_err(),
295 error::Token::Language(biscuit_parser::error::LanguageError::Parameters {
296 missing_parameters: vec!["p4".to_string()],
297 unused_parameters: vec![],
298 })
299 );
300 let mut check = Check::try_from("check if {p4}, {p3}").unwrap();
301 check.set("p3", true).unwrap();
302 let res = builder.clone().check(check);
303 assert_eq!(
304 res.unwrap_err(),
305 error::Token::Language(biscuit_parser::error::LanguageError::Parameters {
306 missing_parameters: vec!["p4".to_string()],
307 unused_parameters: vec![],
308 })
309 );
310 }
311
312 #[test]
313 fn forbid_unbound_parameters_in_set_code() {
314 let builder = BlockBuilder::new();
315 let mut params = HashMap::new();
316 params.insert("p1".to_string(), "hello".into());
317 params.insert("p2".to_string(), 1i64.into());
318 params.insert("p4".to_string(), "this will be ignored".into());
319 let res = builder.code_with_params(
320 r#"fact({p1}, "value");
321 rule($head_var) <- f1($head_var), {p2} > 0;
322 check if {p3};
323 "#,
324 params,
325 HashMap::new(),
326 );
327
328 assert_eq!(
329 res.unwrap_err(),
330 error::Token::Language(biscuit_parser::error::LanguageError::Parameters {
331 missing_parameters: vec!["p3".to_string()],
332 unused_parameters: vec![],
333 })
334 );
335 }
336 #[test]
337 fn empty_set_display() {
338 assert_eq!(Term::Set(BTreeSet::new()).to_string(), "{,}");
339 }
340}