1use std::{
3 collections::{BTreeSet, HashMap},
4 time::{SystemTime, UNIX_EPOCH},
5};
6
7#[cfg(feature = "datalog-macro")]
8use quote::{quote, ToTokens};
9
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum Term {
13 Variable(String),
14 Integer(i64),
15 Str(String),
16 Date(u64),
17 Bytes(Vec<u8>),
18 Bool(bool),
19 Set(BTreeSet<Term>),
20 Parameter(String),
21}
22
23impl From<&Term> for Term {
24 fn from(i: &Term) -> Self {
25 match i {
26 Term::Variable(ref v) => Term::Variable(v.clone()),
27 Term::Integer(ref i) => Term::Integer(*i),
28 Term::Str(ref s) => Term::Str(s.clone()),
29 Term::Date(ref d) => Term::Date(*d),
30 Term::Bytes(ref s) => Term::Bytes(s.clone()),
31 Term::Bool(b) => Term::Bool(*b),
32 Term::Set(ref s) => Term::Set(s.clone()),
33 Term::Parameter(ref p) => Term::Parameter(p.clone()),
34 }
35 }
36}
37
38impl AsRef<Term> for Term {
39 fn as_ref(&self) -> &Term {
40 self
41 }
42}
43
44#[cfg(feature = "datalog-macro")]
45impl ToTokens for Term {
46 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
47 tokens.extend(match self {
48 Term::Variable(v) => quote! { ::biscuit_auth::builder::Term::Variable(#v.to_string()) },
49 Term::Integer(v) => quote! { ::biscuit_auth::builder::Term::Integer(#v) },
50 Term::Str(v) => quote! { ::biscuit_auth::builder::Term::Str(#v.to_string()) },
51 Term::Date(v) => quote! { ::biscuit_auth::builder::Term::Date(#v) },
52 Term::Bool(v) => quote! { ::biscuit_auth::builder::Term::Bool(#v) },
53 Term::Parameter(v) => quote! { ::biscuit_auth::builder::Term::Parameter(#v.to_string()) },
54 Term::Bytes(v) => quote! { ::biscuit_auth::builder::Term::Bytes(<[u8]>::into_vec(Box::new([ #(#v),*]))) },
55 Term::Set(v) => {
56 quote! {{
57 use std::iter::FromIterator;
58 ::biscuit_auth::builder::Term::Set(::std::collections::BTreeSet::from_iter(<[::biscuit_auth::builder::Term]>::into_vec(Box::new([ #(#v),*]))))
59 }}
60 }
61 })
62 }
63}
64
65#[derive(Clone, Debug, Hash, PartialEq, Eq)]
66pub enum Scope {
67 Authority,
68 Previous,
69 PublicKey(PublicKey),
70 Parameter(String),
71}
72
73#[cfg(feature = "datalog-macro")]
74impl ToTokens for Scope {
75 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
76 tokens.extend(match self {
77 Scope::Authority => quote! { ::biscuit_auth::builder::Scope::Authority},
78 Scope::Previous => quote! { ::biscuit_auth::builder::Scope::Previous},
79 Scope::PublicKey(pk) => {
80 let bytes = pk.iter();
81 quote! { ::biscuit_auth::builder::Scope::PublicKey(
82 ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*]).unwrap()
83 )}
84 }
85 Scope::Parameter(v) => {
86 quote! { ::biscuit_auth::builder::Scope::Parameter(#v.to_string())}
87 }
88 })
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Hash, Eq)]
94pub struct Predicate {
95 pub name: String,
96 pub terms: Vec<Term>,
97}
98
99impl Predicate {
100 pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Predicate {
101 Predicate {
102 name,
103 terms: terms.into(),
104 }
105 }
106}
107
108#[cfg(feature = "datalog-macro")]
109impl ToTokens for Predicate {
110 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
111 let name = &self.name;
112 let terms = self.terms.iter();
113 tokens.extend(quote! {
114 ::biscuit_auth::builder::Predicate::new(
115 #name.to_string(),
116 <[::biscuit_auth::builder::Term]>::into_vec(Box::new([#(#terms),*]))
117 )
118 })
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct Fact {
125 pub predicate: Predicate,
126 pub parameters: Option<HashMap<String, Option<Term>>>,
127}
128
129impl Fact {
130 pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Fact {
131 let mut parameters = HashMap::new();
132 let terms: Vec<Term> = terms.into();
133
134 for term in &terms {
135 if let Term::Parameter(name) = &term {
136 parameters.insert(name.to_string(), None);
137 }
138 }
139 Fact {
140 predicate: Predicate::new(name, terms),
141 parameters: Some(parameters),
142 }
143 }
144}
145
146#[cfg(feature = "datalog-macro")]
147impl ToTokens for Fact {
148 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
149 let name = &self.predicate.name;
150 let terms = self.predicate.terms.iter();
151 tokens.extend(quote! {
152 ::biscuit_auth::builder::Fact::new(
153 #name.to_string(),
154 <[::biscuit_auth::builder::Term]>::into_vec(Box::new([#(#terms),*]))
155 )
156 })
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct Expression {
163 pub ops: Vec<Op>,
164}
165
166#[cfg(feature = "datalog-macro")]
167impl ToTokens for Expression {
168 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
169 let ops = self.ops.iter();
170 tokens.extend(quote! {
171 ::biscuit_auth::builder::Expression {
172 ops: <[::biscuit_auth::builder::Op]>::into_vec(Box::new([#(#ops),*]))
173 }
174 });
175 }
176}
177
178#[derive(Debug, Clone, PartialEq, Eq)]
180pub enum Op {
181 Value(Term),
182 Unary(Unary),
183 Binary(Binary),
184}
185
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub enum Unary {
188 Negate,
189 Parens,
190 Length,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
194pub enum Binary {
195 LessThan,
196 GreaterThan,
197 LessOrEqual,
198 GreaterOrEqual,
199 Equal,
200 Contains,
201 Prefix,
202 Suffix,
203 Regex,
204 Add,
205 Sub,
206 Mul,
207 Div,
208 And,
209 Or,
210 Intersection,
211 Union,
212 BitwiseAnd,
213 BitwiseOr,
214 BitwiseXor,
215 NotEqual,
216}
217
218#[cfg(feature = "datalog-macro")]
219impl ToTokens for Op {
220 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
221 tokens.extend(match self {
222 Op::Value(t) => quote! { ::biscuit_auth::builder::Op::Value(#t) },
223 Op::Unary(u) => quote! { ::biscuit_auth::builder::Op::Unary(#u) },
224 Op::Binary(b) => quote! { ::biscuit_auth::builder::Op::Binary(#b) },
225 });
226 }
227}
228
229#[cfg(feature = "datalog-macro")]
230impl ToTokens for Unary {
231 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
232 tokens.extend(match self {
233 Unary::Negate => quote! {::biscuit_auth::datalog::Unary::Negate },
234 Unary::Parens => quote! {::biscuit_auth::datalog::Unary::Parens },
235 Unary::Length => quote! {::biscuit_auth::datalog::Unary::Length },
236 });
237 }
238}
239
240#[cfg(feature = "datalog-macro")]
241impl ToTokens for Binary {
242 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
243 tokens.extend(match self {
244 Binary::LessThan => quote! { ::biscuit_auth::datalog::Binary::LessThan },
245 Binary::GreaterThan => quote! { ::biscuit_auth::datalog::Binary::GreaterThan },
246 Binary::LessOrEqual => quote! { ::biscuit_auth::datalog::Binary::LessOrEqual },
247 Binary::GreaterOrEqual => quote! { ::biscuit_auth::datalog::Binary::GreaterOrEqual },
248 Binary::Equal => quote! { ::biscuit_auth::datalog::Binary::Equal },
249 Binary::Contains => quote! { ::biscuit_auth::datalog::Binary::Contains },
250 Binary::Prefix => quote! { ::biscuit_auth::datalog::Binary::Prefix },
251 Binary::Suffix => quote! { ::biscuit_auth::datalog::Binary::Suffix },
252 Binary::Regex => quote! { ::biscuit_auth::datalog::Binary::Regex },
253 Binary::Add => quote! { ::biscuit_auth::datalog::Binary::Add },
254 Binary::Sub => quote! { ::biscuit_auth::datalog::Binary::Sub },
255 Binary::Mul => quote! { ::biscuit_auth::datalog::Binary::Mul },
256 Binary::Div => quote! { ::biscuit_auth::datalog::Binary::Div },
257 Binary::And => quote! { ::biscuit_auth::datalog::Binary::And },
258 Binary::Or => quote! { ::biscuit_auth::datalog::Binary::Or },
259 Binary::Intersection => quote! { ::biscuit_auth::datalog::Binary::Intersection },
260 Binary::Union => quote! { ::biscuit_auth::datalog::Binary::Union },
261 Binary::BitwiseAnd => quote! { ::biscuit_auth::datalog::Binary::BitwiseAnd },
262 Binary::BitwiseOr => quote! { ::biscuit_auth::datalog::Binary::BitwiseOr },
263 Binary::BitwiseXor => quote! { ::biscuit_auth::datalog::Binary::BitwiseXor },
264 Binary::NotEqual => quote! { ::biscuit_auth::datalog::Binary::NotEqual },
265 });
266 }
267}
268
269pub type PublicKey = Vec<u8>;
270
271#[derive(Debug, Clone, PartialEq, Eq)]
273pub struct Rule {
274 pub head: Predicate,
275 pub body: Vec<Predicate>,
276 pub expressions: Vec<Expression>,
277 pub parameters: Option<HashMap<String, Option<Term>>>,
278 pub scopes: Vec<Scope>,
279 pub scope_parameters: Option<HashMap<String, Option<PublicKey>>>,
280}
281
282impl Rule {
283 pub fn new(
284 head: Predicate,
285 body: Vec<Predicate>,
286 expressions: Vec<Expression>,
287 scopes: Vec<Scope>,
288 ) -> Rule {
289 let mut parameters = HashMap::new();
290 let mut scope_parameters = HashMap::new();
291
292 for term in &head.terms {
293 if let Term::Parameter(name) = &term {
294 parameters.insert(name.to_string(), None);
295 }
296 }
297
298 for predicate in &body {
299 for term in &predicate.terms {
300 if let Term::Parameter(name) = &term {
301 parameters.insert(name.to_string(), None);
302 }
303 }
304 }
305
306 for expression in &expressions {
307 for op in &expression.ops {
308 if let Op::Value(Term::Parameter(name)) = &op {
309 parameters.insert(name.to_string(), None);
310 }
311 }
312 }
313
314 for scope in &scopes {
315 if let Scope::Parameter(name) = &scope {
316 scope_parameters.insert(name.to_string(), None);
317 }
318 }
319
320 Rule {
321 head,
322 body,
323 expressions,
324 parameters: Some(parameters),
325 scopes,
326 scope_parameters: Some(scope_parameters),
327 }
328 }
329
330 pub fn validate_variables(&self) -> Result<(), String> {
331 let mut head_variables: std::collections::HashSet<String> = self
332 .head
333 .terms
334 .iter()
335 .filter_map(|term| match term {
336 Term::Variable(s) => Some(s.to_string()),
337 _ => None,
338 })
339 .collect();
340
341 for predicate in self.body.iter() {
342 for term in predicate.terms.iter() {
343 if let Term::Variable(v) = term {
344 head_variables.remove(v);
345 if head_variables.is_empty() {
346 return Ok(());
347 }
348 }
349 }
350 }
351
352 if head_variables.is_empty() {
353 Ok(())
354 } else {
355 Err(format!(
356 "rule head contains variables that are not used in predicates of the rule's body: {}",
357 head_variables
358 .iter()
359 .map(|s| format!("${}", s))
360 .collect::<Vec<_>>()
361 .join(", ")
362 ))
363 }
364 }
365}
366
367#[cfg(feature = "datalog-macro")]
368impl ToTokens for Rule {
369 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
370 let head = &self.head;
371 let body = self.body.iter();
372 let expressions = self.expressions.iter();
373 let scopes = self.scopes.iter();
374 tokens.extend(quote! {
375 ::biscuit_auth::builder::Rule::new(
376 #head,
377 <[::biscuit_auth::builder::Predicate]>::into_vec(Box::new([#(#body),*])),
378 <[::biscuit_auth::builder::Expression]>::into_vec(Box::new([#(#expressions),*])),
379 <[::biscuit_auth::builder::Scope]>::into_vec(Box::new([#(#scopes),*]))
380 )
381 });
382 }
383}
384
385#[derive(Debug, Clone, PartialEq, Eq)]
387pub struct Check {
388 pub queries: Vec<Rule>,
389 pub kind: CheckKind,
390}
391
392#[derive(Debug, Clone, PartialEq, Eq)]
393pub enum CheckKind {
394 One,
395 All,
396}
397
398#[cfg(feature = "datalog-macro")]
399impl ToTokens for Check {
400 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
401 let queries = self.queries.iter();
402 let kind = &self.kind;
403 tokens.extend(quote! {
404 ::biscuit_auth::builder::Check {
405 queries: <[::biscuit_auth::builder::Rule]>::into_vec(Box::new([#(#queries),*])),
406 kind: #kind,
407 }
408 });
409 }
410}
411
412#[cfg(feature = "datalog-macro")]
413impl ToTokens for CheckKind {
414 fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
415 tokens.extend(match self {
416 CheckKind::One => quote! {
417 ::biscuit_auth::builder::CheckKind::One
418 },
419 CheckKind::All => quote! {
420 ::biscuit_auth::builder::CheckKind::All
421 },
422 });
423 }
424}
425
426#[derive(Debug, Clone, PartialEq, Eq)]
427pub enum PolicyKind {
428 Allow,
429 Deny,
430}
431
432#[cfg(feature = "datalog-macro")]
433impl ToTokens for PolicyKind {
434 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
435 tokens.extend(match self {
436 PolicyKind::Allow => quote! {
437 ::biscuit_auth::builder::PolicyKind::Allow
438 },
439 PolicyKind::Deny => quote! {
440 ::biscuit_auth::builder::PolicyKind::Deny
441 },
442 });
443 }
444}
445
446#[derive(Debug, Clone, PartialEq, Eq)]
448pub struct Policy {
449 pub queries: Vec<Rule>,
450 pub kind: PolicyKind,
451}
452
453#[cfg(feature = "datalog-macro")]
454impl ToTokens for Policy {
455 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
456 let queries = self.queries.iter();
457 let kind = &self.kind;
458 tokens.extend(quote! {
459 ::biscuit_auth::builder::Policy{
460 kind: #kind,
461 queries: <[::biscuit_auth::builder::Rule]>::into_vec(Box::new([#(#queries),*])),
462 }
463 });
464 }
465}
466
467pub fn fact<I: AsRef<Term>>(name: &str, terms: &[I]) -> Fact {
469 let pred = pred(name, terms);
470 Fact::new(pred.name, pred.terms)
471}
472
473pub fn pred<I: AsRef<Term>>(name: &str, terms: &[I]) -> Predicate {
475 Predicate {
476 name: name.to_string(),
477 terms: terms.iter().map(|term| term.as_ref().clone()).collect(),
478 }
479}
480
481pub fn rule<T: AsRef<Term>, P: AsRef<Predicate>>(
483 head_name: &str,
484 head_terms: &[T],
485 predicates: &[P],
486) -> Rule {
487 Rule::new(
488 pred(head_name, head_terms),
489 predicates.iter().map(|p| p.as_ref().clone()).collect(),
490 Vec::new(),
491 vec![],
492 )
493}
494
495pub fn constrained_rule<T: AsRef<Term>, P: AsRef<Predicate>, E: AsRef<Expression>>(
497 head_name: &str,
498 head_terms: &[T],
499 predicates: &[P],
500 expressions: &[E],
501) -> Rule {
502 Rule::new(
503 pred(head_name, head_terms),
504 predicates.iter().map(|p| p.as_ref().clone()).collect(),
505 expressions.iter().map(|c| c.as_ref().clone()).collect(),
506 vec![],
507 )
508}
509
510pub fn check<P: AsRef<Predicate>>(predicates: &[P], kind: CheckKind) -> Check {
512 let empty_terms: &[Term] = &[];
513 Check {
514 queries: vec![Rule::new(
515 pred("query", empty_terms),
516 predicates.iter().map(|p| p.as_ref().clone()).collect(),
517 vec![],
518 vec![],
519 )],
520 kind,
521 }
522}
523
524pub fn int(i: i64) -> Term {
526 Term::Integer(i)
527}
528
529pub fn string(s: &str) -> Term {
531 Term::Str(s.to_string())
532}
533
534pub fn date(t: &SystemTime) -> Term {
538 let dur = t.duration_since(UNIX_EPOCH).unwrap();
539 Term::Date(dur.as_secs())
540}
541
542pub fn var(s: &str) -> Term {
544 Term::Variable(s.to_string())
545}
546
547pub fn variable(s: &str) -> Term {
549 Term::Variable(s.to_string())
550}
551
552pub fn bytes(s: &[u8]) -> Term {
554 Term::Bytes(s.to_vec())
555}
556
557pub fn boolean(b: bool) -> Term {
559 Term::Bool(b)
560}
561
562pub fn set(s: BTreeSet<Term>) -> Term {
564 Term::Set(s)
565}
566
567pub fn parameter(p: &str) -> Term {
569 Term::Parameter(p.to_string())
570}