1use crate::auth::{Permission, Role, User};
2use crate::middleware::{Admin, Authenticated};
3use axum::{
4 extract::State,
5 http::StatusCode,
6 Json,
7};
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10
11use crate::api_v1::AppState;
13
14#[derive(Debug, Deserialize)]
19pub struct RegisterRequest {
20 pub username: String,
21 pub email: String,
22 pub password: String,
23 pub role: Option<Role>,
24 pub tenant_id: Option<String>,
25}
26
27#[derive(Debug, Serialize)]
28pub struct RegisterResponse {
29 pub user_id: Uuid,
30 pub username: String,
31 pub email: String,
32 pub role: Role,
33 pub tenant_id: String,
34}
35
36#[derive(Debug, Deserialize)]
37pub struct LoginRequest {
38 pub username: String,
39 pub password: String,
40}
41
42#[derive(Debug, Serialize)]
43pub struct LoginResponse {
44 pub token: String,
45 pub user: UserInfo,
46}
47
48#[derive(Debug, Serialize)]
49pub struct UserInfo {
50 pub id: Uuid,
51 pub username: String,
52 pub email: String,
53 pub role: Role,
54 pub tenant_id: String,
55}
56
57impl From<User> for UserInfo {
58 fn from(user: User) -> Self {
59 Self {
60 id: user.id,
61 username: user.username,
62 email: user.email,
63 role: user.role,
64 tenant_id: user.tenant_id,
65 }
66 }
67}
68
69#[derive(Debug, Deserialize)]
70pub struct CreateApiKeyRequest {
71 pub name: String,
72 pub role: Option<Role>,
73 pub expires_in_days: Option<i64>,
74}
75
76#[derive(Debug, Serialize)]
77pub struct CreateApiKeyResponse {
78 pub id: Uuid,
79 pub name: String,
80 pub key: String,
81 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
82}
83
84#[derive(Debug, Serialize)]
85pub struct ApiKeyInfo {
86 pub id: Uuid,
87 pub name: String,
88 pub tenant_id: String,
89 pub role: Role,
90 pub created_at: chrono::DateTime<chrono::Utc>,
91 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
92 pub active: bool,
93 pub last_used: Option<chrono::DateTime<chrono::Utc>>,
94}
95
96pub async fn register_handler(
103 State(state): State<AppState>,
104 auth: Option<Authenticated>,
105 Json(req): Json<RegisterRequest>,
106) -> Result<(StatusCode, Json<RegisterResponse>), (StatusCode, String)> {
107 if let Some(auth_ctx) = auth {
109 auth_ctx
110 .0
111 .require_permission(Permission::Admin)
112 .map_err(|_| (StatusCode::FORBIDDEN, "Admin permission required".to_string()))?;
113 }
114
115 let role = req.role.unwrap_or(Role::Developer);
116 let tenant_id = req.tenant_id.unwrap_or_else(|| "default".to_string());
117
118 let user = state
119 .auth_manager
120 .register_user(req.username, req.email, &req.password, role.clone(), tenant_id.clone())
121 .map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
122
123 Ok((
124 StatusCode::CREATED,
125 Json(RegisterResponse {
126 user_id: user.id,
127 username: user.username,
128 email: user.email,
129 role,
130 tenant_id,
131 }),
132 ))
133}
134
135pub async fn login_handler(
138 State(state): State<AppState>,
139 Json(req): Json<LoginRequest>,
140) -> Result<Json<LoginResponse>, (StatusCode, String)> {
141 let token = state
142 .auth_manager
143 .authenticate(&req.username, &req.password)
144 .map_err(|e| (StatusCode::UNAUTHORIZED, e.to_string()))?;
145
146 let user_id = state
148 .auth_manager
149 .validate_token(&token)
150 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
151 .sub
152 .parse::<Uuid>()
153 .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Invalid user ID".to_string()))?;
154
155 let user = state
156 .auth_manager
157 .get_user(&user_id)
158 .ok_or_else(|| (StatusCode::NOT_FOUND, "User not found".to_string()))?;
159
160 Ok(Json(LoginResponse {
161 token,
162 user: user.into(),
163 }))
164}
165
166pub async fn me_handler(
169 State(state): State<AppState>,
170 Authenticated(auth_ctx): Authenticated,
171) -> Result<Json<UserInfo>, (StatusCode, String)> {
172 let user_id = auth_ctx
173 .user_id()
174 .parse::<Uuid>()
175 .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Invalid user ID".to_string()))?;
176
177 let user = state
178 .auth_manager
179 .get_user(&user_id)
180 .ok_or_else(|| (StatusCode::NOT_FOUND, "User not found".to_string()))?;
181
182 Ok(Json(user.into()))
183}
184
185pub async fn create_api_key_handler(
188 State(state): State<AppState>,
189 Authenticated(auth_ctx): Authenticated,
190 Json(req): Json<CreateApiKeyRequest>,
191) -> Result<(StatusCode, Json<CreateApiKeyResponse>), (StatusCode, String)> {
192 let role = req.role.unwrap_or(Role::ServiceAccount);
195
196 auth_ctx
197 .require_permission(Permission::Write)
198 .map_err(|_| (StatusCode::FORBIDDEN, "Write permission required".to_string()))?;
199
200 let expires_at = req.expires_in_days.map(|days| {
201 chrono::Utc::now() + chrono::Duration::days(days)
202 });
203
204 let (api_key, key) = state
205 .auth_manager
206 .create_api_key(req.name.clone(), auth_ctx.tenant_id().to_string(), role, expires_at);
207
208 Ok((
209 StatusCode::CREATED,
210 Json(CreateApiKeyResponse {
211 id: api_key.id,
212 name: req.name,
213 key,
214 expires_at,
215 }),
216 ))
217}
218
219pub async fn list_api_keys_handler(
222 State(state): State<AppState>,
223 Authenticated(auth_ctx): Authenticated,
224) -> Result<Json<Vec<ApiKeyInfo>>, (StatusCode, String)> {
225 let keys = state.auth_manager.list_api_keys(auth_ctx.tenant_id());
226
227 let key_infos: Vec<ApiKeyInfo> = keys
228 .into_iter()
229 .map(|k| ApiKeyInfo {
230 id: k.id,
231 name: k.name,
232 tenant_id: k.tenant_id,
233 role: k.role,
234 created_at: k.created_at,
235 expires_at: k.expires_at,
236 active: k.active,
237 last_used: k.last_used,
238 })
239 .collect();
240
241 Ok(Json(key_infos))
242}
243
244pub async fn revoke_api_key_handler(
247 State(state): State<AppState>,
248 Authenticated(auth_ctx): Authenticated,
249 axum::extract::Path(key_id): axum::extract::Path<Uuid>,
250) -> Result<StatusCode, (StatusCode, String)> {
251 auth_ctx
252 .require_permission(Permission::Write)
253 .map_err(|_| (StatusCode::FORBIDDEN, "Write permission required".to_string()))?;
254
255 state
256 .auth_manager
257 .revoke_api_key(&key_id)
258 .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
259
260 Ok(StatusCode::NO_CONTENT)
261}
262
263pub async fn list_users_handler(
266 State(state): State<AppState>,
267 Admin(_): Admin,
268) -> Json<Vec<UserInfo>> {
269 let users = state.auth_manager.list_users();
270 Json(users.into_iter().map(UserInfo::from).collect())
271}
272
273pub async fn delete_user_handler(
276 State(state): State<AppState>,
277 Admin(_): Admin,
278 axum::extract::Path(user_id): axum::extract::Path<Uuid>,
279) -> Result<StatusCode, (StatusCode, String)> {
280 state
281 .auth_manager
282 .delete_user(&user_id)
283 .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
284
285 Ok(StatusCode::NO_CONTENT)
286}