simple_zanzibar/
lib.rs

1//! A simplified Rust implementation of Google's Zanzibar authorization system.
2
3pub mod error;
4pub mod eval;
5pub mod model;
6pub mod parser;
7pub mod store;
8
9use crate::error::ZanzibarError;
10use crate::model::{NamespaceConfig, Object, Relation, RelationTuple, User};
11use crate::store::{InMemoryTupleStore, TupleStore};
12use std::collections::{HashMap, HashSet};
13
14/// The main service for handling Zanzibar authorization checks.
15pub struct ZanzibarService {
16    configs: HashMap<String, NamespaceConfig>,
17    store: Box<dyn TupleStore>,
18}
19
20impl Default for ZanzibarService {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl ZanzibarService {
27    /// Creates a new service with an in-memory store.
28    pub fn new() -> Self {
29        ZanzibarService {
30            configs: HashMap::new(),
31            store: Box::new(InMemoryTupleStore::default()),
32        }
33    }
34
35    /// Parses a DSL string and adds the resulting configurations to the service.
36    pub fn add_dsl(&mut self, dsl: &str) -> Result<(), ZanzibarError> {
37        let configs = parser::parse_dsl(dsl)?;
38        for config in configs {
39            self.add_config(config);
40        }
41        Ok(())
42    }
43
44    /// Adds or updates a namespace configuration.
45    pub fn add_config(&mut self, config: NamespaceConfig) {
46        self.configs.insert(config.name.clone(), config);
47    }
48
49    /// Writes a relation tuple to the store.
50    pub fn write_tuple(&mut self, tuple: RelationTuple) -> Result<(), ZanzibarError> {
51        self.store
52            .write_tuple(tuple)
53            .map_err(ZanzibarError::StorageError)
54    }
55
56    /// Deletes a relation tuple from the store.
57    pub fn delete_tuple(&mut self, tuple: &RelationTuple) -> Result<(), ZanzibarError> {
58        self.store
59            .delete_tuple(tuple)
60            .map_err(ZanzibarError::StorageError)
61    }
62
63    /// Checks if a user has a specific relation to an object.
64    pub fn check(
65        &self,
66        object: &Object,
67        relation: &Relation,
68        user: &User,
69    ) -> Result<bool, ZanzibarError> {
70        let config = self
71            .configs
72            .get(&object.namespace)
73            .ok_or_else(|| ZanzibarError::NamespaceNotFound(object.namespace.clone()))?;
74
75        eval::check(
76            object,
77            relation,
78            user,
79            config,
80            self.store.as_ref(),
81            &mut HashSet::new(),
82        )
83    }
84
85    /// Expands the userset for a given object and relation.
86    pub fn expand(
87        &self,
88        object: &Object,
89        relation: &Relation,
90    ) -> Result<model::ExpandedUserset, ZanzibarError> {
91        let config = self
92            .configs
93            .get(&object.namespace)
94            .ok_or_else(|| ZanzibarError::NamespaceNotFound(object.namespace.clone()))?;
95
96        eval::expand(object, relation, config, self.store.as_ref())
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    #[test]
103    fn it_works() {
104        assert_eq!(2 + 2, 4);
105    }
106}