rocketmq_auth/authentication/evaluator.rs
1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Authentication evaluator - facade for authentication logic.
16//!
17//! This module provides the `AuthenticationEvaluator` which acts as the main entry point
18//! for authentication evaluation, coordinating authentication strategies.
19
20use crate::authentication::strategy::AuthenticationStrategy;
21use crate::authorization::context::authentication_context::AuthenticationContext;
22use crate::AuthServiceResult;
23
24/// Authentication evaluator - main entry point for authentication.
25///
26/// This struct acts as a facade, delegating authentication evaluation
27/// to the configured authentication strategy.
28///
29/// # Design
30///
31/// - **Facade Pattern**: Simplifies authentication API for clients
32/// - **Strategy Pattern**: Delegates to pluggable authentication strategies
33/// - **Thread-safe**: Can be safely shared across threads
34///
35/// # Example
36///
37/// ```rust,ignore
38/// use rocketmq_auth::AuthenticationEvaluator;
39/// use rocketmq_auth::StatelessAuthenticationStrategy;
40/// use rocketmq_auth::AuthConfig;
41///
42/// let config = AuthConfig::default();
43/// let strategy = StatelessAuthenticationStrategy::new(config, None);
44/// let evaluator = AuthenticationEvaluator::new(strategy);
45///
46/// // Evaluate authentication
47/// evaluator.evaluate(&context)?;
48/// ```
49pub struct AuthenticationEvaluator<S>
50where
51 S: AuthenticationStrategy,
52{
53 authentication_strategy: S,
54}
55
56impl<S> AuthenticationEvaluator<S>
57where
58 S: AuthenticationStrategy,
59{
60 /// Create a new authentication evaluator with the given strategy.
61 ///
62 /// # Arguments
63 ///
64 /// * `authentication_strategy` - The authentication strategy to use
65 ///
66 /// # Example
67 ///
68 /// ```rust,ignore
69 /// let strategy = StatelessAuthenticationStrategy::new(config, provider);
70 /// let evaluator = AuthenticationEvaluator::new(strategy);
71 /// ```
72 pub fn new(authentication_strategy: S) -> Self {
73 Self {
74 authentication_strategy,
75 }
76 }
77
78 /// Evaluate authentication for the given context.
79 ///
80 /// This method delegates to the underlying authentication strategy.
81 /// Returns `Ok(())` if authentication succeeds, or an `AuthServiceError` if it fails.
82 ///
83 /// # Arguments
84 ///
85 /// * `context` - The authentication context containing request information
86 ///
87 /// # Returns
88 ///
89 /// * `Ok(())` - Authentication succeeded
90 /// * `Err(AuthServiceError)` - Authentication failed with a specific error
91 ///
92 /// # Example
93 ///
94 /// ```rust,ignore
95 /// let context = DefaultAuthenticationContext::new();
96 /// evaluator.evaluate(&context)?;
97 /// ```
98 pub async fn evaluate(&self, context: &dyn AuthenticationContext) -> AuthServiceResult<()> {
99 self.authentication_strategy.authenticate(context).await
100 }
101
102 /// Get a reference to the underlying authentication strategy.
103 pub fn strategy(&self) -> &S {
104 &self.authentication_strategy
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::authentication::context::default_authentication_context::DefaultAuthenticationContext;
112 use crate::authentication::strategy::allow_all::AllowAllAuthenticationStrategy;
113
114 struct TestStrategy {
115 should_succeed: bool,
116 }
117
118 impl AuthenticationStrategy for TestStrategy {
119 fn authenticate<'a>(
120 &'a self,
121 _context: &'a dyn AuthenticationContext,
122 ) -> crate::authentication::strategy::AuthenticationFuture<'a> {
123 Box::pin(async move {
124 if self.should_succeed {
125 Ok(())
126 } else {
127 Err(crate::AuthServiceError::new(
128 crate::AuthOperation::Authenticate,
129 crate::AuthFailureKind::Unauthenticated,
130 ))
131 }
132 })
133 }
134 }
135
136 #[test]
137 fn test_evaluator_creation() {
138 let strategy = AllowAllAuthenticationStrategy;
139 let evaluator = AuthenticationEvaluator::new(strategy);
140
141 assert!(std::ptr::eq(&evaluator.authentication_strategy, evaluator.strategy()));
142 }
143
144 #[tokio::test]
145 async fn test_evaluate_success() {
146 let strategy = TestStrategy { should_succeed: true };
147 let evaluator = AuthenticationEvaluator::new(strategy);
148 let context = DefaultAuthenticationContext::new();
149
150 let result = evaluator.evaluate(&context).await;
151 assert!(result.is_ok());
152 }
153
154 #[tokio::test]
155 async fn test_evaluate_failure() {
156 let strategy = TestStrategy { should_succeed: false };
157 let evaluator = AuthenticationEvaluator::new(strategy);
158 let context = DefaultAuthenticationContext::new();
159
160 let result = evaluator.evaluate(&context).await;
161 assert!(result.is_err());
162
163 let error = result.expect_err("authentication must fail");
164 assert_eq!(error.operation(), crate::AuthOperation::Authenticate);
165 assert_eq!(error.kind(), crate::AuthFailureKind::Unauthenticated);
166 }
167
168 #[tokio::test]
169 async fn test_evaluate_with_allow_all_strategy() {
170 let strategy = AllowAllAuthenticationStrategy;
171 let evaluator = AuthenticationEvaluator::new(strategy);
172 let context = DefaultAuthenticationContext::new();
173
174 let result = evaluator.evaluate(&context).await;
175 assert!(result.is_ok());
176 }
177}