1use async_trait::async_trait;
4
5use crate::error::AuthzError;
6use crate::resolver::{CheckResult, ResolveCheckRequest};
7
8#[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
29pub 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}