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(
182 State(state): State<AppState>,
183 Authenticated(auth_ctx): Authenticated,
184) -> Result<Json<UserInfo>, (StatusCode, String)> {
185 let user_id = auth_ctx.user_id().parse::<Uuid>().map_err(|_| {
186 (
187 StatusCode::INTERNAL_SERVER_ERROR,
188 "Invalid user ID".to_string(),
189 )
190 })?;
191
192 let user = state
193 .auth_manager
194 .get_user(&user_id)
195 .ok_or_else(|| (StatusCode::NOT_FOUND, "User not found".to_string()))?;
196
197 Ok(Json(user.into()))
198}
199
200pub async fn create_api_key_handler(
203 State(state): State<AppState>,
204 Authenticated(auth_ctx): Authenticated,
205 Json(req): Json<CreateApiKeyRequest>,
206) -> Result<(StatusCode, Json<CreateApiKeyResponse>), (StatusCode, String)> {
207 let role = req.role.unwrap_or(Role::ServiceAccount);
210
211 auth_ctx
212 .require_permission(Permission::Write)
213 .map_err(|_| {
214 (
215 StatusCode::FORBIDDEN,
216 "Write permission required".to_string(),
217 )
218 })?;
219
220 let expires_at = req
221 .expires_in_days
222 .map(|days| chrono::Utc::now() + chrono::Duration::days(days));
223
224 let (api_key, key) = state.auth_manager.create_api_key(
225 req.name.clone(),
226 auth_ctx.tenant_id().to_string(),
227 role,
228 expires_at,
229 );
230
231 Ok((
232 StatusCode::CREATED,
233 Json(CreateApiKeyResponse {
234 id: api_key.id,
235 name: req.name,
236 key,
237 expires_at,
238 }),
239 ))
240}
241
242pub async fn list_api_keys_handler(
245 State(state): State<AppState>,
246 Authenticated(auth_ctx): Authenticated,
247) -> Result<Json<Vec<ApiKeyInfo>>, (StatusCode, String)> {
248 let keys = state.auth_manager.list_api_keys(auth_ctx.tenant_id());
249
250 let key_infos: Vec<ApiKeyInfo> = keys
251 .into_iter()
252 .map(|k| ApiKeyInfo {
253 id: k.id,
254 name: k.name,
255 tenant_id: k.tenant_id,
256 role: k.role,
257 created_at: k.created_at,
258 expires_at: k.expires_at,
259 active: k.active,
260 last_used: k.last_used,
261 })
262 .collect();
263
264 Ok(Json(key_infos))
265}
266
267pub async fn revoke_api_key_handler(
270 State(state): State<AppState>,
271 Authenticated(auth_ctx): Authenticated,
272 axum::extract::Path(key_id): axum::extract::Path<Uuid>,
273) -> Result<StatusCode, (StatusCode, String)> {
274 auth_ctx
275 .require_permission(Permission::Write)
276 .map_err(|_| {
277 (
278 StatusCode::FORBIDDEN,
279 "Write permission required".to_string(),
280 )
281 })?;
282
283 state
284 .auth_manager
285 .revoke_api_key(&key_id)
286 .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
287
288 Ok(StatusCode::NO_CONTENT)
289}
290
291pub async fn list_users_handler(
294 State(state): State<AppState>,
295 Admin(_): Admin,
296) -> Json<Vec<UserInfo>> {
297 let users = state.auth_manager.list_users();
298 Json(users.into_iter().map(UserInfo::from).collect())
299}
300
301pub async fn delete_user_handler(
304 State(state): State<AppState>,
305 Admin(_): Admin,
306 axum::extract::Path(user_id): axum::extract::Path<Uuid>,
307) -> Result<StatusCode, (StatusCode, String)> {
308 state
309 .auth_manager
310 .delete_user(&user_id)
311 .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
312
313 Ok(StatusCode::NO_CONTENT)
314}