rocketmq_auth/authorization/chain/
handler_chain.rs1use std::sync::Arc;
21
22use rocketmq_security_api::AuthorizationDecision;
23#[cfg(test)]
24use rocketmq_security_api::AuthorizationDenial;
25
26use super::handler::AuthorizationHandler;
27use crate::authorization::context::default_authorization_context::DefaultAuthorizationContext;
28use crate::AuthServiceError;
29use crate::AuthServiceResult;
30
31pub struct AuthorizationHandlerChain {
53 handlers: Vec<Arc<dyn AuthorizationHandler>>,
54}
55
56impl AuthorizationHandlerChain {
57 pub fn new() -> Self {
59 Self { handlers: Vec::new() }
60 }
61
62 pub fn add_handler(mut self, handler: Arc<dyn AuthorizationHandler>) -> Self {
64 self.handlers.push(handler);
65 self
66 }
67
68 pub async fn handle(&self, context: &DefaultAuthorizationContext) -> AuthServiceResult<AuthorizationDecision> {
78 if self.handlers.is_empty() {
79 return Err(AuthServiceError::not_initialized(
80 "no authorization handlers configured".to_owned(),
81 ));
82 }
83
84 let mut denial = None;
85 for handler in &self.handlers {
86 match handler.handle(context).await {
87 Ok(AuthorizationDecision::Allow) => return Ok(AuthorizationDecision::Allow),
88 Ok(AuthorizationDecision::Deny(reason)) => denial.get_or_insert(reason),
89 Err(error) => return Err(error),
90 };
91 }
92
93 denial.map_or_else(
94 || {
95 Err(AuthServiceError::not_initialized(
96 "authorization handler chain did not produce a decision".to_owned(),
97 ))
98 },
99 |reason| Ok(AuthorizationDecision::Deny(reason)),
100 )
101 }
102
103 pub fn len(&self) -> usize {
105 self.handlers.len()
106 }
107
108 pub fn is_empty(&self) -> bool {
110 self.handlers.is_empty()
111 }
112}
113
114impl Default for AuthorizationHandlerChain {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use std::future::Future;
123 use std::pin::Pin;
124 use std::sync::atomic::AtomicUsize;
125 use std::sync::atomic::Ordering;
126 use std::sync::Arc;
127
128 use rocketmq_security_api::Action;
129
130 use super::*;
131 use crate::authentication::enums::subject_type::SubjectType;
132 use crate::authorization::chain::handler::AuthorizationHandler;
133 use crate::authorization::context::default_authorization_context::DefaultAuthorizationContext;
134 use crate::authorization::model::resource::Resource;
135
136 struct AllowHandler {
137 call_count: Arc<AtomicUsize>,
138 }
139
140 impl AuthorizationHandler for AllowHandler {
141 fn handle<'a>(
142 &'a self,
143 _context: &'a DefaultAuthorizationContext,
144 ) -> Pin<Box<dyn Future<Output = AuthServiceResult<AuthorizationDecision>> + Send + 'a>> {
145 self.call_count.fetch_add(1, Ordering::SeqCst);
146 Box::pin(async move { Ok(AuthorizationDecision::Allow) })
147 }
148 }
149
150 struct DenyHandler {
151 call_count: Arc<AtomicUsize>,
152 }
153
154 impl AuthorizationHandler for DenyHandler {
155 fn handle<'a>(
156 &'a self,
157 _context: &'a DefaultAuthorizationContext,
158 ) -> Pin<Box<dyn Future<Output = AuthServiceResult<AuthorizationDecision>> + Send + 'a>> {
159 self.call_count.fetch_add(1, Ordering::SeqCst);
160 Box::pin(async move { Ok(AuthorizationDecision::Deny(AuthorizationDenial::PermissionDenied)) })
161 }
162 }
163
164 struct FailureHandler {
165 call_count: Arc<AtomicUsize>,
166 }
167
168 impl AuthorizationHandler for FailureHandler {
169 fn handle<'a>(
170 &'a self,
171 _context: &'a DefaultAuthorizationContext,
172 ) -> Pin<Box<dyn Future<Output = AuthServiceResult<AuthorizationDecision>> + Send + 'a>> {
173 self.call_count.fetch_add(1, Ordering::SeqCst);
174 Box::pin(async move { Err(AuthServiceError::invalid_context("broken context".to_owned())) })
175 }
176 }
177
178 #[tokio::test]
179 async fn test_chain_single_handler_allow() {
180 let counter = Arc::new(AtomicUsize::new(0));
181 let handler = Arc::new(AllowHandler {
182 call_count: counter.clone(),
183 });
184
185 let chain = AuthorizationHandlerChain::new().add_handler(handler);
186
187 let context = DefaultAuthorizationContext::of(
188 "user",
189 SubjectType::User,
190 Resource::of_topic("test"),
191 Action::Pub,
192 "127.0.0.1",
193 );
194
195 let result = chain.handle(&context).await;
196 assert_eq!(result.unwrap(), AuthorizationDecision::Allow);
197 assert_eq!(counter.load(Ordering::SeqCst), 1);
198 }
199
200 #[tokio::test]
201 async fn test_chain_single_handler_deny() {
202 let counter = Arc::new(AtomicUsize::new(0));
203 let handler = Arc::new(DenyHandler {
204 call_count: counter.clone(),
205 });
206
207 let chain = AuthorizationHandlerChain::new().add_handler(handler);
208
209 let context = DefaultAuthorizationContext::of(
210 "user",
211 SubjectType::User,
212 Resource::of_topic("test"),
213 Action::Pub,
214 "127.0.0.1",
215 );
216
217 let result = chain.handle(&context).await;
218 assert_eq!(
219 result.unwrap(),
220 AuthorizationDecision::Deny(AuthorizationDenial::PermissionDenied)
221 );
222 assert_eq!(counter.load(Ordering::SeqCst), 1);
223 }
224
225 #[tokio::test]
226 async fn test_chain_multiple_handlers_first_allow() {
227 let counter1 = Arc::new(AtomicUsize::new(0));
228 let counter2 = Arc::new(AtomicUsize::new(0));
229
230 let chain = AuthorizationHandlerChain::new()
231 .add_handler(Arc::new(AllowHandler {
232 call_count: counter1.clone(),
233 }))
234 .add_handler(Arc::new(DenyHandler {
235 call_count: counter2.clone(),
236 }));
237
238 let context = DefaultAuthorizationContext::of(
239 "user",
240 SubjectType::User,
241 Resource::of_topic("test"),
242 Action::Pub,
243 "127.0.0.1",
244 );
245
246 let result = chain.handle(&context).await;
247 assert_eq!(result.unwrap(), AuthorizationDecision::Allow);
248 assert_eq!(counter1.load(Ordering::SeqCst), 1);
249 assert_eq!(counter2.load(Ordering::SeqCst), 0); }
251
252 #[tokio::test]
253 async fn test_chain_deny_can_be_followed_by_allow() {
254 let counter1 = Arc::new(AtomicUsize::new(0));
255 let counter2 = Arc::new(AtomicUsize::new(0));
256
257 let chain = AuthorizationHandlerChain::new()
258 .add_handler(Arc::new(DenyHandler {
259 call_count: counter1.clone(),
260 }))
261 .add_handler(Arc::new(AllowHandler {
262 call_count: counter2.clone(),
263 }));
264
265 let context = DefaultAuthorizationContext::of(
266 "user",
267 SubjectType::User,
268 Resource::of_topic("test"),
269 Action::Pub,
270 "127.0.0.1",
271 );
272
273 let result = chain.handle(&context).await;
274 assert_eq!(result.unwrap(), AuthorizationDecision::Allow);
275 assert_eq!(counter1.load(Ordering::SeqCst), 1);
276 assert_eq!(counter2.load(Ordering::SeqCst), 1);
277 }
278
279 #[tokio::test]
280 async fn test_chain_all_deny() {
281 let counter1 = Arc::new(AtomicUsize::new(0));
282 let counter2 = Arc::new(AtomicUsize::new(0));
283
284 let chain = AuthorizationHandlerChain::new()
285 .add_handler(Arc::new(DenyHandler {
286 call_count: counter1.clone(),
287 }))
288 .add_handler(Arc::new(DenyHandler {
289 call_count: counter2.clone(),
290 }));
291
292 let context = DefaultAuthorizationContext::of(
293 "user",
294 SubjectType::User,
295 Resource::of_topic("test"),
296 Action::Pub,
297 "127.0.0.1",
298 );
299
300 let result = chain.handle(&context).await;
301 assert_eq!(
302 result.unwrap(),
303 AuthorizationDecision::Deny(AuthorizationDenial::PermissionDenied)
304 );
305 assert_eq!(counter1.load(Ordering::SeqCst), 1);
306 assert_eq!(counter2.load(Ordering::SeqCst), 1);
307 }
308
309 #[tokio::test]
310 async fn test_chain_operational_failure_is_terminal() {
311 let failure_count = Arc::new(AtomicUsize::new(0));
312 let allow_count = Arc::new(AtomicUsize::new(0));
313 let chain = AuthorizationHandlerChain::new()
314 .add_handler(Arc::new(FailureHandler {
315 call_count: failure_count.clone(),
316 }))
317 .add_handler(Arc::new(AllowHandler {
318 call_count: allow_count.clone(),
319 }));
320 let context = DefaultAuthorizationContext::default();
321
322 let error = chain.handle(&context).await.unwrap_err();
323 assert_eq!(error.kind(), crate::AuthFailureKind::InvalidInput);
324 assert_eq!(failure_count.load(Ordering::SeqCst), 1);
325 assert_eq!(allow_count.load(Ordering::SeqCst), 0);
326 }
327
328 #[tokio::test]
329 async fn test_empty_chain() {
330 let chain = AuthorizationHandlerChain::new();
331
332 let context = DefaultAuthorizationContext::of(
333 "user",
334 SubjectType::User,
335 Resource::of_topic("test"),
336 Action::Pub,
337 "127.0.0.1",
338 );
339
340 let result = chain.handle(&context).await;
341 assert!(result.is_err());
342 }
343}