allsource_core/infrastructure/web/
auth_api.rs1use crate::infrastructure::security::{
2 auth::{Permission, Role, User},
3 middleware::{Admin, Authenticated, OptionalAuth},
4};
5use axum::{Json, extract::State, http::StatusCode};
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9use crate::infrastructure::web::api_v1::AppState;
11
12#[derive(Debug, Deserialize)]
17pub struct RegisterRequest {
18 pub username: String,
19 pub email: String,
20 pub password: String,
21 pub role: Option<Role>,
22 pub tenant_id: Option<String>,
23}
24
25#[derive(Debug, Serialize)]
26pub struct RegisterResponse {
27 pub user_id: Uuid,
28 pub username: String,
29 pub email: String,
30 pub role: Role,
31 pub tenant_id: String,
32}
33
34#[derive(Debug, Deserialize)]
35pub struct LoginRequest {
36 pub username: String,
37 pub password: String,
38}
39
40#[derive(Debug, Serialize)]
41pub struct LoginResponse {
42 pub token: String,
43 pub user: UserInfo,
44}
45
46#[derive(Debug, Serialize)]
47pub struct UserInfo {
48 pub id: Uuid,
49 pub username: String,
50 pub email: String,
51 pub role: Role,
52 pub tenant_id: String,
53}
54
55impl From<User> for UserInfo {
56 fn from(user: User) -> Self {
57 Self {
58 id: user.id,
59 username: user.username,
60 email: user.email,
61 role: user.role,
62 tenant_id: user.tenant_id,
63 }
64 }
65}
66
67#[derive(Debug, Deserialize)]
68pub struct CreateApiKeyRequest {
69 pub name: String,
70 pub role: Option<Role>,
71 pub expires_in_days: Option<i64>,
72}
73
74#[derive(Debug, Serialize)]
75pub struct CreateApiKeyResponse {
76 pub id: Uuid,
77 pub name: String,
78 pub key: String,
79 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
80}
81
82#[derive(Debug, Serialize)]
83pub struct ApiKeyInfo {
84 pub id: Uuid,
85 pub name: String,
86 pub tenant_id: String,
87 pub role: Role,
88 pub created_at: chrono::DateTime<chrono::Utc>,
89 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
90 pub active: bool,
91 pub last_used: Option<chrono::DateTime<chrono::Utc>>,
92}
93
94pub async fn register_handler(
101 State(state): State<AppState>,
102 OptionalAuth(auth): OptionalAuth,
103 Json(req): Json<RegisterRequest>,
104) -> Result<(StatusCode, Json<RegisterResponse>), (StatusCode, String)> {
105 if let Some(auth_ctx) = auth {
107 auth_ctx
108 .require_permission(Permission::Admin)
109 .map_err(|_| {
110 (
111 StatusCode::FORBIDDEN,
112 "Admin permission required".to_string(),
113 )
114 })?;
115 }
116
117 let role = req.role.unwrap_or(Role::Developer);
118 let tenant_id = req.tenant_id.unwrap_or_else(|| "default".to_string());
119
120 let user = state
121 .auth_manager
122 .register_user(
123 req.username,
124 req.email,
125 &req.password,
126 role.clone(),
127 tenant_id.clone(),
128 )
129 .map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
130
131 Ok((
132 StatusCode::CREATED,
133 Json(RegisterResponse {
134 user_id: user.id,
135 username: user.username,
136 email: user.email,
137 role,
138 tenant_id,
139 }),
140 ))
141}
142
143pub async fn login_handler(
146 State(state): State<AppState>,
147 Json(req): Json<LoginRequest>,
148) -> Result<Json<LoginResponse>, (StatusCode, String)> {
149 let token = state
150 .auth_manager
151 .authenticate(&req.username, &req.password)
152 .map_err(|e| (StatusCode::UNAUTHORIZED, e.to_string()))?;
153
154 let user_id = state
156 .auth_manager
157 .validate_token(&token)
158 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
159 .sub
160 .parse::<Uuid>()
161 .map_err(|_| {
162 (
163 StatusCode::INTERNAL_SERVER_ERROR,
164 "Invalid user ID".to_string(),
165 )
166 })?;
167
168 let user = state
169 .auth_manager
170 .get_user(&user_id)
171 .ok_or_else(|| (StatusCode::NOT_FOUND, "User not found".to_string()))?;
172
173 Ok(Json(LoginResponse {
174 token,
175 user: user.into(),
176 }))
177}
178
179pub async fn me_handler(
187 State(state): State<AppState>,
188 Authenticated(auth_ctx): Authenticated,
189) -> Result<Json<UserInfo>, (StatusCode, String)> {
190 if let Ok(user_id) = auth_ctx.user_id().parse::<Uuid>()
192 && let Some(user) = state.auth_manager.get_user(&user_id)
193 {
194 return Ok(Json(user.into()));
195 }
196
197 Ok(Json(UserInfo {
201 id: auth_ctx.user_id().parse::<Uuid>().unwrap_or_default(),
202 username: auth_ctx.user_id().to_string(),
203 email: String::new(),
204 role: auth_ctx.claims.role.clone(),
205 tenant_id: auth_ctx.tenant_id().to_string(),
206 }))
207}
208
209pub async fn create_api_key_handler(
212 State(state): State<AppState>,
213 Authenticated(auth_ctx): Authenticated,
214 Json(req): Json<CreateApiKeyRequest>,
215) -> Result<(StatusCode, Json<CreateApiKeyResponse>), (StatusCode, String)> {
216 let role = req.role.unwrap_or(Role::ServiceAccount);
219
220 auth_ctx
221 .require_permission(Permission::Write)
222 .map_err(|_| {
223 (
224 StatusCode::FORBIDDEN,
225 "Write permission required".to_string(),
226 )
227 })?;
228
229 let expires_at = req
230 .expires_in_days
231 .map(|days| chrono::Utc::now() + chrono::Duration::days(days));
232
233 let (api_key, key) = state.auth_manager.create_api_key(
234 req.name.clone(),
235 auth_ctx.tenant_id().to_string(),
236 role,
237 expires_at,
238 );
239
240 Ok((
241 StatusCode::CREATED,
242 Json(CreateApiKeyResponse {
243 id: api_key.id,
244 name: req.name,
245 key,
246 expires_at,
247 }),
248 ))
249}
250
251pub async fn list_api_keys_handler(
254 State(state): State<AppState>,
255 Authenticated(auth_ctx): Authenticated,
256) -> Result<Json<Vec<ApiKeyInfo>>, (StatusCode, String)> {
257 let keys = state.auth_manager.list_api_keys(auth_ctx.tenant_id());
258
259 let key_infos: Vec<ApiKeyInfo> = keys
260 .into_iter()
261 .map(|k| ApiKeyInfo {
262 id: k.id,
263 name: k.name,
264 tenant_id: k.tenant_id,
265 role: k.role,
266 created_at: k.created_at,
267 expires_at: k.expires_at,
268 active: k.active,
269 last_used: k.last_used,
270 })
271 .collect();
272
273 Ok(Json(key_infos))
274}
275
276pub async fn revoke_api_key_handler(
279 State(state): State<AppState>,
280 Authenticated(auth_ctx): Authenticated,
281 axum::extract::Path(key_id): axum::extract::Path<Uuid>,
282) -> Result<StatusCode, (StatusCode, String)> {
283 auth_ctx
284 .require_permission(Permission::Write)
285 .map_err(|_| {
286 (
287 StatusCode::FORBIDDEN,
288 "Write permission required".to_string(),
289 )
290 })?;
291
292 state
293 .auth_manager
294 .revoke_api_key(&key_id)
295 .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
296
297 Ok(StatusCode::NO_CONTENT)
298}
299
300pub async fn list_users_handler(
303 State(state): State<AppState>,
304 Admin(_): Admin,
305) -> Json<Vec<UserInfo>> {
306 let users = state.auth_manager.list_users();
307 Json(users.into_iter().map(UserInfo::from).collect())
308}
309
310pub async fn delete_user_handler(
313 State(state): State<AppState>,
314 Admin(_): Admin,
315 axum::extract::Path(user_id): axum::extract::Path<Uuid>,
316) -> Result<StatusCode, (StatusCode, String)> {
317 state
318 .auth_manager
319 .delete_user(&user_id)
320 .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
321
322 Ok(StatusCode::NO_CONTENT)
323}