Skip to main content

authz_core/
dispatcher.rs

1//! Dispatcher trait and LocalDispatcher stub.
2
3use async_trait::async_trait;
4
5use crate::error::AuthzError;
6use crate::resolver::{CheckResult, ResolveCheckRequest};
7
8/// Dispatches check, list_objects, and list_subjects requests.
9#[async_trait]
10pub trait Dispatcher: Send + Sync {
11    async fn dispatch_check(&self, request: ResolveCheckRequest)
12    -> Result<CheckResult, AuthzError>;
13    async fn dispatch_list_objects(
14        &self,
15        _subject_type: &str,
16        _subject_id: &str,
17        _relation: &str,
18        _object_type: &str,
19    ) -> Result<Vec<String>, AuthzError>;
20    async fn dispatch_list_subjects(
21        &self,
22        _object_type: &str,
23        _object_id: &str,
24        _relation: &str,
25        _subject_type: &str,
26    ) -> Result<Vec<String>, AuthzError>;
27}
28
29/// Local dispatcher that calls resolver directly.
30pub struct LocalDispatcher<R> {
31    resolver: R,
32}
33
34impl<R> LocalDispatcher<R> {
35    pub fn new(resolver: R) -> Self {
36        Self { resolver }
37    }
38}
39
40#[async_trait]
41impl<R> Dispatcher for LocalDispatcher<R>
42where
43    R: crate::resolver::CheckResolver + Send + Sync,
44{
45    async fn dispatch_check(
46        &self,
47        request: ResolveCheckRequest,
48    ) -> Result<CheckResult, AuthzError> {
49        self.resolver.resolve_check(request).await
50    }
51
52    async fn dispatch_list_objects(
53        &self,
54        _subject_type: &str,
55        _subject_id: &str,
56        _relation: &str,
57        _object_type: &str,
58    ) -> Result<Vec<String>, AuthzError> {
59        Ok(vec![])
60    }
61
62    async fn dispatch_list_subjects(
63        &self,
64        _object_type: &str,
65        _object_id: &str,
66        _relation: &str,
67        _subject_type: &str,
68    ) -> Result<Vec<String>, AuthzError> {
69        Ok(vec![])
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::resolver::{CheckResolver, CheckResult, ResolveCheckRequest};
77
78    struct StubResolver;
79    #[async_trait::async_trait]
80    impl CheckResolver for StubResolver {
81        async fn resolve_check(&self, _: ResolveCheckRequest) -> Result<CheckResult, AuthzError> {
82            Ok(CheckResult::Allowed)
83        }
84    }
85
86    #[tokio::test]
87    async fn local_dispatcher_dispatch_check() {
88        let d = LocalDispatcher::new(StubResolver);
89        let req = ResolveCheckRequest::new(
90            "doc".into(),
91            "1".into(),
92            "viewer".into(),
93            "user".into(),
94            "alice".into(),
95        );
96        let r = d.dispatch_check(req).await.unwrap();
97        assert_eq!(r, CheckResult::Allowed);
98    }
99
100    struct DeniedResolver;
101    #[async_trait::async_trait]
102    impl CheckResolver for DeniedResolver {
103        async fn resolve_check(&self, _: ResolveCheckRequest) -> Result<CheckResult, AuthzError> {
104            Ok(CheckResult::Denied)
105        }
106    }
107
108    #[tokio::test]
109    async fn test_local_dispatcher_denied() {
110        let d = LocalDispatcher::new(DeniedResolver);
111        let req = ResolveCheckRequest::new(
112            "doc".into(),
113            "1".into(),
114            "viewer".into(),
115            "user".into(),
116            "bob".into(),
117        );
118        let r = d.dispatch_check(req).await.unwrap();
119        assert_eq!(r, CheckResult::Denied);
120    }
121
122    #[tokio::test]
123    async fn test_local_dispatcher_list_objects_stub() {
124        let d = LocalDispatcher::new(StubResolver);
125        let result = d
126            .dispatch_list_objects("user", "alice", "viewer", "document")
127            .await
128            .unwrap();
129        assert_eq!(result, Vec::<String>::new());
130    }
131}