1use std::{
7 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
8 time::{SystemTime, UNIX_EPOCH},
9};
10
11#[cfg(feature = "datalog-macro")]
12use quote::{quote, ToTokens};
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub enum Term {
17 Variable(String),
18 Integer(i64),
19 Str(String),
20 Date(u64),
21 Bytes(Vec<u8>),
22 Bool(bool),
23 Set(BTreeSet<Term>),
24 Parameter(String),
25 Null,
26 Array(Vec<Term>),
27 Map(BTreeMap<MapKey, Term>),
28}
29
30impl Term {
31 fn extract_parameters(&self, parameters: &mut HashMap<String, Option<Term>>) {
32 match self {
33 Term::Parameter(name) => {
34 parameters.insert(name.to_string(), None);
35 }
36 Term::Set(s) => {
37 for term in s {
38 term.extract_parameters(parameters);
39 }
40 }
41 Term::Array(a) => {
42 for term in a {
43 term.extract_parameters(parameters);
44 }
45 }
46 Term::Map(m) => {
47 for (key, term) in m {
48 if let MapKey::Parameter(name) = key {
49 parameters.insert(name.to_string(), None);
50 }
51 term.extract_parameters(parameters);
52 }
53 }
54 _ => {}
55 }
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub enum MapKey {
61 Parameter(String),
62 Integer(i64),
63 Str(String),
64}
65
66impl From<&Term> for Term {
67 fn from(i: &Term) -> Self {
68 match i {
69 Term::Variable(ref v) => Term::Variable(v.clone()),
70 Term::Integer(ref i) => Term::Integer(*i),
71 Term::Str(ref s) => Term::Str(s.clone()),
72 Term::Date(ref d) => Term::Date(*d),
73 Term::Bytes(ref s) => Term::Bytes(s.clone()),
74 Term::Bool(b) => Term::Bool(*b),
75 Term::Set(ref s) => Term::Set(s.clone()),
76 Term::Parameter(ref p) => Term::Parameter(p.clone()),
77 Term::Null => Term::Null,
78 Term::Array(ref a) => Term::Array(a.clone()),
79 Term::Map(ref m) => Term::Map(m.clone()),
80 }
81 }
82}
83
84impl AsRef<Term> for Term {
85 fn as_ref(&self) -> &Term {
86 self
87 }
88}
89
90#[cfg(feature = "datalog-macro")]
91impl ToTokens for Term {
92 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
93 tokens.extend(match self {
94 Term::Variable(v) => quote! { ::biscuit_auth::builder::Term::Variable(#v.to_string()) },
95 Term::Integer(v) => quote! { ::biscuit_auth::builder::Term::Integer(#v) },
96 Term::Str(v) => quote! { ::biscuit_auth::builder::Term::Str(#v.to_string()) },
97 Term::Date(v) => quote! { ::biscuit_auth::builder::Term::Date(#v) },
98 Term::Bool(v) => quote! { ::biscuit_auth::builder::Term::Bool(#v) },
99 Term::Parameter(v) => quote! { ::biscuit_auth::builder::Term::Parameter(#v.to_string()) },
100 Term::Bytes(v) => quote! { ::biscuit_auth::builder::Term::Bytes(<[u8]>::into_vec(Box::new([ #(#v),*]))) },
101 Term::Set(v) => {
102 quote! {{
103 use std::iter::FromIterator;
104 ::biscuit_auth::builder::Term::Set(::std::collections::BTreeSet::from_iter(<[::biscuit_auth::builder::Term]>::into_vec(Box::new([ #(#v),*]))))
105 }}
106 }
107 Term::Null => quote! { ::biscuit_auth::builder::Term::Null },
108 Term::Array(v) => {
109 quote! {{
110 use std::iter::FromIterator;
111 ::biscuit_auth::builder::Term::Array(::std::vec::Vec::from_iter(<[::biscuit_auth::builder::Term]>::into_vec( Box::new([ #(#v),*]))))
112 }}
113 }
114 Term::Map(m) => {
115 let it = m.iter().map(|(key, term)| MapEntry {key, term });
116 quote! {{
117 use std::iter::FromIterator;
118 ::biscuit_auth::builder::Term::Map(::std::collections::BTreeMap::from_iter(<[(::biscuit_auth::builder::MapKey,::biscuit_auth::builder::Term)]>::into_vec(Box::new([ #(#it),*]))))
119 }}
120 }
121 })
122 }
123}
124
125#[cfg(feature = "datalog-macro")]
126struct MapEntry<'a> {
127 key: &'a MapKey,
128 term: &'a Term,
129}
130
131#[cfg(feature = "datalog-macro")]
132impl<'a> ToTokens for MapEntry<'a> {
133 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
134 let term = self.term;
135 tokens.extend(match self.key {
136 MapKey::Parameter(p) => {
137 quote! { (::biscuit_auth::builder::MapKey::Parameter(#p.to_string()) , #term )}
138 }
139 MapKey::Integer(i) => {
140 quote! { (::biscuit_auth::builder::MapKey::Integer(#i) , #term )}
141 }
142 MapKey::Str(s) => {
143 quote! { (::biscuit_auth::builder::MapKey::Str(#s.to_string()) , #term )}
144 }
145 });
146 }
147}
148
149#[derive(Clone, Debug, Hash, PartialEq, Eq)]
150pub enum Scope {
151 Authority,
152 Previous,
153 PublicKey(PublicKey),
154 Parameter(String),
155}
156
157#[cfg(feature = "datalog-macro")]
158impl ToTokens for Scope {
159 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
160 tokens.extend(match self {
161 Scope::Authority => quote! { ::biscuit_auth::builder::Scope::Authority},
162 Scope::Previous => quote! { ::biscuit_auth::builder::Scope::Previous},
163 Scope::PublicKey(pk) => {
164 let bytes = pk.key.iter();
165 match pk.algorithm {
166 Algorithm::Ed25519 => quote! { ::biscuit_auth::builder::Scope::PublicKey(
167 ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*], ::biscuit_auth::builder::Algorithm::Ed25519).unwrap()
168 )},
169 Algorithm::Secp256r1 => quote! { ::biscuit_auth::builder::Scope::PublicKey(
170 ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*], ::biscuit_auth::builder::Algorithm::Secp256r1).unwrap()
171 )},
172 }
173 }
174 Scope::Parameter(v) => {
175 quote! { ::biscuit_auth::builder::Scope::Parameter(#v.to_string())}
176 }
177 })
178 }
179}
180
181#[derive(Debug, Clone, PartialEq, Hash, Eq)]
183pub struct Predicate {
184 pub name: String,
185 pub terms: Vec<Term>,
186}
187
188impl Predicate {
189 pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Predicate {
190 Predicate {
191 name,
192 terms: terms.into(),
193 }
194 }
195}
196
197#[cfg(feature = "datalog-macro")]
198impl ToTokens for Predicate {
199 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
200 let name = &self.name;
201 let terms = self.terms.iter();
202 tokens.extend(quote! {
203 ::biscuit_auth::builder::Predicate::new(
204 #name.to_string(),
205 <[::biscuit_auth::builder::Term]>::into_vec(Box::new([#(#terms),*]))
206 )
207 })
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct Fact {
214 pub predicate: Predicate,
215 pub parameters: Option<HashMap<String, Option<Term>>>,
216}
217
218impl Fact {
219 pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Fact {
220 let mut parameters = HashMap::new();
221 let terms: Vec<Term> = terms.into();
222
223 for term in &terms {
224 term.extract_parameters(&mut parameters);
225 }
226 Fact {
227 predicate: Predicate::new(name, terms),
228 parameters: Some(parameters),
229 }
230 }
231}
232
233#[cfg(feature = "datalog-macro")]
234impl ToTokens for Fact {
235 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
236 let name = &self.predicate.name;
237 let terms = self.predicate.terms.iter();
238 tokens.extend(quote! {
239 ::biscuit_auth::builder::Fact::new(
240 #name.to_string(),
241 <[::biscuit_auth::builder::Term]>::into_vec(Box::new([#(#terms),*]))
242 )
243 })
244 }
245}
246
247#[derive(Debug, Clone, PartialEq, Eq)]
249pub struct Expression {
250 pub ops: Vec<Op>,
251}
252
253#[cfg(feature = "datalog-macro")]
254impl ToTokens for Expression {
255 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
256 let ops = self.ops.iter();
257 tokens.extend(quote! {
258 ::biscuit_auth::builder::Expression {
259 ops: <[::biscuit_auth::builder::Op]>::into_vec(Box::new([#(#ops),*]))
260 }
261 });
262 }
263}
264
265#[derive(Debug, Clone, PartialEq, Eq)]
267pub enum Op {
268 Value(Term),
269 Unary(Unary),
270 Binary(Binary),
271 Closure(Vec<String>, Vec<Op>),
272}
273
274impl Op {
275 fn collect_parameters(&self, parameters: &mut HashMap<String, Option<Term>>) {
276 match self {
277 Op::Value(term) => {
278 term.extract_parameters(parameters);
279 }
280 Op::Closure(_, ops) => {
281 for op in ops {
282 op.collect_parameters(parameters);
283 }
284 }
285 _ => {}
286 }
287 }
288}
289
290#[derive(Debug, Clone, PartialEq, Eq)]
291pub enum Unary {
292 Negate,
293 Parens,
294 Length,
295 TypeOf,
296 Ffi(String),
297}
298
299#[derive(Debug, Clone, PartialEq, Eq)]
300pub enum Binary {
301 LessThan,
302 GreaterThan,
303 LessOrEqual,
304 GreaterOrEqual,
305 Equal,
306 Contains,
307 Prefix,
308 Suffix,
309 Regex,
310 Add,
311 Sub,
312 Mul,
313 Div,
314 And,
315 Or,
316 Intersection,
317 Union,
318 BitwiseAnd,
319 BitwiseOr,
320 BitwiseXor,
321 NotEqual,
322 HeterogeneousEqual,
323 HeterogeneousNotEqual,
324 LazyAnd,
325 LazyOr,
326 All,
327 Any,
328 Get,
329 Ffi(String),
330 TryOr,
331}
332
333#[cfg(feature = "datalog-macro")]
334impl ToTokens for Op {
335 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
336 tokens.extend(match self {
337 Op::Value(t) => quote! { ::biscuit_auth::builder::Op::Value(#t) },
338 Op::Unary(u) => quote! { ::biscuit_auth::builder::Op::Unary(#u) },
339 Op::Binary(b) => quote! { ::biscuit_auth::builder::Op::Binary(#b) },
340 Op::Closure(params, os) => quote! {
341 ::biscuit_auth::builder::Op::Closure(
342 <[String]>::into_vec(Box::new([#(#params.to_string()),*])),
343 <[::biscuit_auth::builder::Op]>::into_vec(Box::new([#(#os),*]))
344 )
345 },
346 });
347 }
348}
349
350#[cfg(feature = "datalog-macro")]
351impl ToTokens for Unary {
352 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
353 tokens.extend(match self {
354 Unary::Negate => quote! {::biscuit_auth::builder::Unary::Negate },
355 Unary::Parens => quote! {::biscuit_auth::builder::Unary::Parens },
356 Unary::Length => quote! {::biscuit_auth::builder::Unary::Length },
357 Unary::TypeOf => quote! {::biscuit_auth::builder::Unary::TypeOf },
358 Unary::Ffi(name) => quote! {::biscuit_auth::builder::Unary::Ffi(#name.to_string()) },
359 });
360 }
361}
362
363#[cfg(feature = "datalog-macro")]
364impl ToTokens for Binary {
365 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
366 tokens.extend(match self {
367 Binary::LessThan => quote! { ::biscuit_auth::builder::Binary::LessThan },
368 Binary::GreaterThan => quote! { ::biscuit_auth::builder::Binary::GreaterThan },
369 Binary::LessOrEqual => quote! { ::biscuit_auth::builder::Binary::LessOrEqual },
370 Binary::GreaterOrEqual => quote! { ::biscuit_auth::builder::Binary::GreaterOrEqual },
371 Binary::Equal => quote! { ::biscuit_auth::builder::Binary::Equal },
372 Binary::Contains => quote! { ::biscuit_auth::builder::Binary::Contains },
373 Binary::Prefix => quote! { ::biscuit_auth::builder::Binary::Prefix },
374 Binary::Suffix => quote! { ::biscuit_auth::builder::Binary::Suffix },
375 Binary::Regex => quote! { ::biscuit_auth::builder::Binary::Regex },
376 Binary::Add => quote! { ::biscuit_auth::builder::Binary::Add },
377 Binary::Sub => quote! { ::biscuit_auth::builder::Binary::Sub },
378 Binary::Mul => quote! { ::biscuit_auth::builder::Binary::Mul },
379 Binary::Div => quote! { ::biscuit_auth::builder::Binary::Div },
380 Binary::And => quote! { ::biscuit_auth::builder::Binary::And },
381 Binary::Or => quote! { ::biscuit_auth::builder::Binary::Or },
382 Binary::Intersection => quote! { ::biscuit_auth::builder::Binary::Intersection },
383 Binary::Union => quote! { ::biscuit_auth::builder::Binary::Union },
384 Binary::BitwiseAnd => quote! { ::biscuit_auth::builder::Binary::BitwiseAnd },
385 Binary::BitwiseOr => quote! { ::biscuit_auth::builder::Binary::BitwiseOr },
386 Binary::BitwiseXor => quote! { ::biscuit_auth::builder::Binary::BitwiseXor },
387 Binary::NotEqual => quote! { ::biscuit_auth::builder::Binary::NotEqual },
388 Binary::HeterogeneousEqual => {
389 quote! { ::biscuit_auth::builder::Binary::HeterogeneousEqual}
390 }
391 Binary::HeterogeneousNotEqual => {
392 quote! { ::biscuit_auth::builder::Binary::HeterogeneousNotEqual}
393 }
394 Binary::LazyAnd => quote! { ::biscuit_auth::builder::Binary::LazyAnd },
395 Binary::LazyOr => quote! { ::biscuit_auth::builder::Binary::LazyOr },
396 Binary::All => quote! { ::biscuit_auth::builder::Binary::All },
397 Binary::Any => quote! { ::biscuit_auth::builder::Binary::Any },
398 Binary::Get => quote! { ::biscuit_auth::builder::Binary::Get },
399 Binary::Ffi(name) => quote! {::biscuit_auth::builder::Binary::Ffi(#name.to_string()) },
400 Binary::TryOr => quote! { ::biscuit_auth::builder::Binary::TryOr },
401 });
402 }
403}
404
405#[derive(Debug, Clone, Hash, PartialEq, Eq)]
406pub struct PublicKey {
407 pub key: Vec<u8>,
408 pub algorithm: Algorithm,
409}
410
411#[derive(Debug, Clone, Hash, PartialEq, Eq)]
412pub enum Algorithm {
413 Ed25519,
414 Secp256r1,
415}
416
417#[derive(Debug, Clone, PartialEq, Eq)]
419pub struct Rule {
420 pub head: Predicate,
421 pub body: Vec<Predicate>,
422 pub expressions: Vec<Expression>,
423 pub parameters: Option<HashMap<String, Option<Term>>>,
424 pub scopes: Vec<Scope>,
425 pub scope_parameters: Option<HashMap<String, Option<PublicKey>>>,
426}
427
428impl Rule {
429 pub fn new(
430 head: Predicate,
431 body: Vec<Predicate>,
432 expressions: Vec<Expression>,
433 scopes: Vec<Scope>,
434 ) -> Rule {
435 let mut parameters = HashMap::new();
436 let mut scope_parameters = HashMap::new();
437
438 for term in &head.terms {
439 term.extract_parameters(&mut parameters);
440 }
441
442 for predicate in &body {
443 for term in &predicate.terms {
444 term.extract_parameters(&mut parameters);
445 }
446 }
447
448 for expression in &expressions {
449 for op in &expression.ops {
450 op.collect_parameters(&mut parameters);
451 }
452 }
453
454 for scope in &scopes {
455 if let Scope::Parameter(name) = &scope {
456 scope_parameters.insert(name.to_string(), None);
457 }
458 }
459
460 Rule {
461 head,
462 body,
463 expressions,
464 parameters: Some(parameters),
465 scopes,
466 scope_parameters: Some(scope_parameters),
467 }
468 }
469
470 pub fn validate_variables(&self) -> Result<(), String> {
471 let mut free_variables: HashSet<String> = HashSet::default();
472 for term in self.head.terms.iter() {
473 if let Term::Variable(s) = term {
474 free_variables.insert(s.to_string());
475 }
476 }
477
478 for e in self.expressions.iter() {
479 for op in e.ops.iter() {
480 if let Op::Value(Term::Variable(s)) = op {
481 free_variables.insert(s.to_string());
482 }
483 }
484 }
485
486 for predicate in self.body.iter() {
487 for term in predicate.terms.iter() {
488 if let Term::Variable(v) = term {
489 free_variables.remove(v);
490 if free_variables.is_empty() {
491 return Ok(());
492 }
493 }
494 }
495 }
496
497 if free_variables.is_empty() {
498 Ok(())
499 } else {
500 Err(format!(
501 "the rule contains variables that are not bound by predicates in the rule's body: {}",
502 free_variables
503 .iter()
504 .map(|s| format!("${}", s))
505 .collect::<Vec<_>>()
506 .join(", ")
507 ))
508 }
509 }
510}
511
512#[cfg(feature = "datalog-macro")]
513impl ToTokens for Rule {
514 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
515 let head = &self.head;
516 let body = self.body.iter();
517 let expressions = self.expressions.iter();
518 let scopes = self.scopes.iter();
519 tokens.extend(quote! {
520 ::biscuit_auth::builder::Rule::new(
521 #head,
522 <[::biscuit_auth::builder::Predicate]>::into_vec(Box::new([#(#body),*])),
523 <[::biscuit_auth::builder::Expression]>::into_vec(Box::new([#(#expressions),*])),
524 <[::biscuit_auth::builder::Scope]>::into_vec(Box::new([#(#scopes),*]))
525 )
526 });
527 }
528}
529
530#[derive(Debug, Clone, PartialEq, Eq)]
532pub struct Check {
533 pub queries: Vec<Rule>,
534 pub kind: CheckKind,
535}
536
537#[derive(Debug, Clone, PartialEq, Eq)]
538pub enum CheckKind {
539 One,
540 All,
541 Reject,
542}
543
544#[cfg(feature = "datalog-macro")]
545impl ToTokens for Check {
546 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
547 let queries = self.queries.iter();
548 let kind = &self.kind;
549 tokens.extend(quote! {
550 ::biscuit_auth::builder::Check {
551 queries: <[::biscuit_auth::builder::Rule]>::into_vec(Box::new([#(#queries),*])),
552 kind: #kind,
553 }
554 });
555 }
556}
557
558#[cfg(feature = "datalog-macro")]
559impl ToTokens for CheckKind {
560 fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
561 tokens.extend(match self {
562 CheckKind::One => quote! {
563 ::biscuit_auth::builder::CheckKind::One
564 },
565 CheckKind::All => quote! {
566 ::biscuit_auth::builder::CheckKind::All
567 },
568 CheckKind::Reject => quote! {
569 ::biscuit_auth::builder::CheckKind::Reject
570 },
571 });
572 }
573}
574
575#[derive(Debug, Clone, PartialEq, Eq)]
576pub enum PolicyKind {
577 Allow,
578 Deny,
579}
580
581#[cfg(feature = "datalog-macro")]
582impl ToTokens for PolicyKind {
583 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
584 tokens.extend(match self {
585 PolicyKind::Allow => quote! {
586 ::biscuit_auth::builder::PolicyKind::Allow
587 },
588 PolicyKind::Deny => quote! {
589 ::biscuit_auth::builder::PolicyKind::Deny
590 },
591 });
592 }
593}
594
595#[derive(Debug, Clone, PartialEq, Eq)]
597pub struct Policy {
598 pub queries: Vec<Rule>,
599 pub kind: PolicyKind,
600}
601
602#[cfg(feature = "datalog-macro")]
603impl ToTokens for Policy {
604 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
605 let queries = self.queries.iter();
606 let kind = &self.kind;
607 tokens.extend(quote! {
608 ::biscuit_auth::builder::Policy{
609 kind: #kind,
610 queries: <[::biscuit_auth::builder::Rule]>::into_vec(Box::new([#(#queries),*])),
611 }
612 });
613 }
614}
615
616pub fn fact<I: AsRef<Term>>(name: &str, terms: &[I]) -> Fact {
618 let pred = pred(name, terms);
619 Fact::new(pred.name, pred.terms)
620}
621
622pub fn pred<I: AsRef<Term>>(name: &str, terms: &[I]) -> Predicate {
624 Predicate {
625 name: name.to_string(),
626 terms: terms.iter().map(|term| term.as_ref().clone()).collect(),
627 }
628}
629
630pub fn rule<T: AsRef<Term>>(head_name: &str, head_terms: &[T], predicates: &[Predicate]) -> Rule {
632 Rule::new(
633 pred(head_name, head_terms),
634 predicates.to_vec(),
635 Vec::new(),
636 vec![],
637 )
638}
639
640pub fn constrained_rule<T: AsRef<Term>>(
642 head_name: &str,
643 head_terms: &[T],
644 predicates: &[Predicate],
645 expressions: &[Expression],
646) -> Rule {
647 Rule::new(
648 pred(head_name, head_terms),
649 predicates.to_vec(),
650 expressions.to_vec(),
651 vec![],
652 )
653}
654
655pub fn check<P: AsRef<Predicate>>(predicates: &[P], kind: CheckKind) -> Check {
657 let empty_terms: &[Term] = &[];
658 Check {
659 queries: vec![Rule::new(
660 pred("query", empty_terms),
661 predicates.iter().map(|p| p.as_ref().clone()).collect(),
662 vec![],
663 vec![],
664 )],
665 kind,
666 }
667}
668
669pub fn int(i: i64) -> Term {
671 Term::Integer(i)
672}
673
674pub fn string(s: &str) -> Term {
676 Term::Str(s.to_string())
677}
678
679pub fn date(t: &SystemTime) -> Term {
683 let dur = t.duration_since(UNIX_EPOCH).unwrap();
684 Term::Date(dur.as_secs())
685}
686
687pub fn var(s: &str) -> Term {
689 Term::Variable(s.to_string())
690}
691
692pub fn variable(s: &str) -> Term {
694 Term::Variable(s.to_string())
695}
696
697pub fn bytes(s: &[u8]) -> Term {
699 Term::Bytes(s.to_vec())
700}
701
702pub fn boolean(b: bool) -> Term {
704 Term::Bool(b)
705}
706
707pub fn set(s: BTreeSet<Term>) -> Term {
709 Term::Set(s)
710}
711
712pub fn null() -> Term {
714 Term::Null
715}
716
717pub fn array(a: Vec<Term>) -> Term {
719 Term::Array(a)
720}
721
722pub fn map(m: BTreeMap<MapKey, Term>) -> Term {
724 Term::Map(m)
725}
726
727pub fn parameter(p: &str) -> Term {
729 Term::Parameter(p.to_string())
730}