metabase_api_rs/api/
auth_adapter.rs1use super::auth::{AuthManager, Credentials as ApiCredentials};
7use crate::core::error::{Error, Result};
8use crate::core::models::User;
9use crate::transport::auth_traits::{
10 AuthProvider, AuthResponse, Credentials as TransportCredentials,
11};
12use async_trait::async_trait;
13use secrecy::ExposeSecret;
14use std::sync::Arc;
15use tokio::sync::RwLock;
16
17pub struct AuthManagerAdapter {
19 inner: Arc<RwLock<AuthManager>>,
20 http_provider: Arc<dyn AuthProvider>,
21}
22
23impl AuthManagerAdapter {
24 pub fn new(auth_manager: AuthManager, http_provider: Arc<dyn AuthProvider>) -> Self {
26 Self {
27 inner: Arc::new(RwLock::new(auth_manager)),
28 http_provider,
29 }
30 }
31
32 pub async fn inner(&self) -> tokio::sync::RwLockReadGuard<'_, AuthManager> {
34 self.inner.read().await
35 }
36
37 pub async fn inner_mut(&self) -> tokio::sync::RwLockWriteGuard<'_, AuthManager> {
39 self.inner.write().await
40 }
41
42 #[allow(dead_code)]
44 fn convert_credentials(creds: &ApiCredentials) -> TransportCredentials {
45 match creds {
46 ApiCredentials::EmailPassword { email, password } => {
47 TransportCredentials::EmailPassword {
48 email: email.clone(),
49 password: password.expose_secret().to_string(),
50 }
51 }
52 ApiCredentials::ApiKey { key } => {
53 TransportCredentials::ApiKey(key.expose_secret().to_string())
54 }
55 }
56 }
57}
58
59#[async_trait]
60impl AuthProvider for AuthManagerAdapter {
61 async fn authenticate(&self, credentials: &TransportCredentials) -> Result<AuthResponse> {
62 let response = self.http_provider.authenticate(credentials).await?;
64
65 let mut manager = self.inner.write().await;
67 manager.set_session_with_ttl(
68 response.session_token.clone(),
69 response.user.clone(),
70 response.expires_in,
71 );
72
73 Ok(response)
74 }
75
76 async fn refresh_session(&self, session_token: &str) -> Result<AuthResponse> {
77 let response = self.http_provider.refresh_session(session_token).await?;
79
80 let mut manager = self.inner.write().await;
82 manager.set_session_with_ttl(
83 response.session_token.clone(),
84 response.user.clone(),
85 response.expires_in,
86 );
87
88 Ok(response)
89 }
90
91 async fn validate_token(&self, session_token: &str) -> Result<bool> {
92 let manager = self.inner.read().await;
94 if let Some(current_token) = manager.session_token() {
95 if current_token != session_token {
96 return Ok(false);
97 }
98 if !manager.is_authenticated() {
99 return Ok(false);
100 }
101 } else {
102 return Ok(false);
103 }
104 drop(manager); self.http_provider.validate_token(session_token).await
108 }
109
110 async fn logout(&self, session_token: &str) -> Result<()> {
111 self.http_provider.logout(session_token).await?;
113
114 let mut manager = self.inner.write().await;
116 manager.clear_session();
117
118 Ok(())
119 }
120
121 async fn get_user(&self, session_token: &str) -> Result<User> {
122 let manager = self.inner.read().await;
124 if let Some(user) = manager.current_user() {
125 if let Some(current_token) = manager.session_token() {
126 if current_token == session_token {
127 return Ok(user.clone());
128 }
129 }
130 }
131 drop(manager); let user = self.http_provider.get_user(session_token).await?;
135
136 let mut manager = self.inner.write().await;
138 if let Some(current_token) = manager.session_token() {
139 if current_token == session_token {
140 manager.set_session(session_token.to_string(), user.clone());
141 }
142 }
143
144 Ok(user)
145 }
146}
147
148pub struct AuthManagerAdapterBuilder {
150 auth_manager: Option<AuthManager>,
151 http_provider: Option<Arc<dyn AuthProvider>>,
152}
153
154impl Default for AuthManagerAdapterBuilder {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl AuthManagerAdapterBuilder {
161 pub fn new() -> Self {
163 Self {
164 auth_manager: None,
165 http_provider: None,
166 }
167 }
168
169 pub fn auth_manager(mut self, manager: AuthManager) -> Self {
171 self.auth_manager = Some(manager);
172 self
173 }
174
175 pub fn http_provider(mut self, provider: Arc<dyn AuthProvider>) -> Self {
177 self.http_provider = Some(provider);
178 self
179 }
180
181 pub fn build(self) -> Result<AuthManagerAdapter> {
183 let auth_manager = self.auth_manager.unwrap_or_default();
184 let http_provider = self
185 .http_provider
186 .ok_or_else(|| Error::Config("HTTP provider is required".to_string()))?;
187
188 Ok(AuthManagerAdapter::new(auth_manager, http_provider))
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use crate::transport::auth_traits::MockAuthProvider;
196
197 #[tokio::test]
198 async fn test_adapter_authentication() {
199 let mock_provider = Arc::new(MockAuthProvider::default());
201
202 let auth_manager = AuthManager::new();
204
205 let adapter = AuthManagerAdapter::new(auth_manager, mock_provider);
207
208 let credentials = TransportCredentials::EmailPassword {
210 email: "test@example.com".to_string(),
211 password: "password123".to_string(),
212 };
213
214 let response = adapter.authenticate(&credentials).await.unwrap();
215 assert_eq!(response.session_token, "mock_session_token_123");
216
217 let manager = adapter.inner().await;
219 assert!(manager.is_authenticated());
220 assert_eq!(manager.session_token(), Some("mock_session_token_123"));
221 }
222
223 #[tokio::test]
224 async fn test_adapter_logout() {
225 let mock_provider = Arc::new(MockAuthProvider::default());
227
228 let mut auth_manager = AuthManager::new();
230 auth_manager.set_session(
231 "test_token".to_string(),
232 User {
233 id: crate::core::models::common::UserId(1),
234 email: "test@example.com".to_string(),
235 first_name: "Test".to_string(),
236 last_name: "User".to_string(),
237 is_active: true,
238 is_superuser: false,
239 is_qbnewb: false,
240 date_joined: chrono::Utc::now(),
241 last_login: None,
242 locale: None,
243 google_auth: false,
244 ldap_auth: false,
245 common_name: Some("Test User".to_string()),
246 group_ids: Vec::new(),
247 login_attributes: None,
248 user_group_memberships: Vec::new(),
249 },
250 );
251
252 let adapter = AuthManagerAdapter::new(auth_manager, mock_provider);
254
255 {
257 let manager = adapter.inner().await;
258 assert!(manager.is_authenticated());
259 }
260
261 adapter.logout("test_token").await.unwrap();
263
264 {
266 let manager = adapter.inner().await;
267 assert!(!manager.is_authenticated());
268 assert!(manager.session_token().is_none());
269 }
270 }
271}