metabase_api_rs/service/
auth.rs1use super::traits::{Service, ServiceError, ServiceResult};
6use crate::api::auth::Credentials;
7use crate::core::models::User;
8use crate::transport::http_provider_safe::{HttpProviderExt, HttpProviderSafe};
9use async_trait::async_trait;
10use secrecy::ExposeSecret;
11use serde::Deserialize;
12use serde_json::json;
13use std::sync::Arc;
14
15#[async_trait]
17pub trait AuthService: Service {
18 async fn authenticate(&self, credentials: Credentials) -> ServiceResult<(String, User)>;
20
21 async fn logout(&self, session_id: &str) -> ServiceResult<()>;
23
24 async fn get_current_user(&self, session_id: &str) -> ServiceResult<User>;
26
27 async fn validate_session(&self, session_id: &str) -> ServiceResult<bool>;
29
30 async fn health_check(&self) -> ServiceResult<crate::core::models::HealthStatus>;
32}
33
34pub struct HttpAuthService {
36 http_provider: Arc<dyn HttpProviderSafe>,
37}
38
39impl HttpAuthService {
40 pub fn new(http_provider: Arc<dyn HttpProviderSafe>) -> Self {
42 Self { http_provider }
43 }
44}
45
46#[async_trait]
47impl Service for HttpAuthService {
48 fn name(&self) -> &str {
49 "AuthService"
50 }
51}
52
53#[async_trait]
54impl AuthService for HttpAuthService {
55 async fn authenticate(&self, credentials: Credentials) -> ServiceResult<(String, User)> {
56 let request_body = match &credentials {
57 Credentials::EmailPassword { email, password } => {
58 json!({
59 "username": email,
60 "password": password.expose_secret()
61 })
62 }
63 Credentials::ApiKey { key } => {
64 json!({
65 "api_key": key.expose_secret()
66 })
67 }
68 };
69
70 #[derive(Deserialize)]
71 struct SessionResponse {
72 id: String,
73 #[serde(flatten)]
74 user_data: serde_json::Value,
75 }
76
77 let response: SessionResponse = self
78 .http_provider
79 .post("/api/session", &request_body)
80 .await
81 .map_err(ServiceError::from)?;
82
83 use crate::core::models::common::UserId;
85
86 let user = User {
87 id: UserId(response.user_data["id"].as_i64().unwrap_or(1)),
88 email: response.user_data["email"]
89 .as_str()
90 .unwrap_or("unknown@example.com")
91 .to_string(),
92 first_name: response.user_data["first_name"]
93 .as_str()
94 .unwrap_or("Unknown")
95 .to_string(),
96 last_name: response.user_data["last_name"]
97 .as_str()
98 .unwrap_or("User")
99 .to_string(),
100 is_superuser: response.user_data["is_superuser"]
101 .as_bool()
102 .unwrap_or(false),
103 is_active: true,
104 is_qbnewb: response.user_data["is_qbnewb"].as_bool().unwrap_or(false),
105 date_joined: chrono::Utc::now(),
106 last_login: Some(chrono::Utc::now()),
107 common_name: None,
108 group_ids: Vec::new(),
109 locale: response.user_data["locale"].as_str().map(|s| s.to_string()),
110 google_auth: response.user_data["google_auth"].as_bool().unwrap_or(false),
111 ldap_auth: response.user_data["ldap_auth"].as_bool().unwrap_or(false),
112 login_attributes: None,
113 user_group_memberships: Vec::new(),
114 };
115
116 Ok((response.id, user))
117 }
118
119 async fn logout(&self, _session_id: &str) -> ServiceResult<()> {
120 self.http_provider
121 .delete_json("/api/session")
122 .await
123 .map(|_: serde_json::Value| ())
124 .map_err(ServiceError::from)
125 }
126
127 async fn get_current_user(&self, _session_id: &str) -> ServiceResult<User> {
128 self.http_provider
129 .get("/api/user/current")
130 .await
131 .map_err(ServiceError::from)
132 }
133
134 async fn validate_session(&self, session_id: &str) -> ServiceResult<bool> {
135 match self.get_current_user(session_id).await {
137 Ok(_) => Ok(true),
138 Err(_) => Ok(false),
139 }
140 }
141
142 async fn health_check(&self) -> ServiceResult<crate::core::models::HealthStatus> {
143 self.http_provider
144 .get("/api/health")
145 .await
146 .map_err(ServiceError::from)
147 }
148}