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