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