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