Skip to main content

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}