From 654b55a2a42979072e8e932f84112fa1a6e34426 Mon Sep 17 00:00:00 2001 From: schn33fuchs Date: Sat, 2 May 2026 19:15:40 +0200 Subject: [PATCH] Put some api calls behind admin access --- backend/src/cookie/validation.rs | 83 ++++++++++++++++++++++++++++++++ backend/src/router.rs | 17 +++++-- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/backend/src/cookie/validation.rs b/backend/src/cookie/validation.rs index 7fe057b..9994cd4 100644 --- a/backend/src/cookie/validation.rs +++ b/backend/src/cookie/validation.rs @@ -93,3 +93,86 @@ pub async fn validate_token( request.extensions_mut().insert(filter_user(&user)); Ok(next.run(request).await) } + +pub async fn validate_admin( + cookies: CookieJar, + State(data): State>, + mut request: Request, + next: Next, +) -> Result)> { + let token = cookies + .get("token") + .map(|cookie| cookie.value().to_string()) + .or_else(|| { + request + .headers() + .get(header::AUTHORIZATION) + .and_then(|header| header.to_str().ok()) + .and_then(|value| { + if value.starts_with("Bearer ") { + Some(value[7..].to_owned()) + } else { + None + } + }) + }); + + let token = token.ok_or_else(|| { + let error = json!({ + "status": "error", + "message": "Please provide a valid token" + }); + (StatusCode::UNAUTHORIZED, Json(error)) + })?; + + let claims = decode_token( + token, + &DecodingKey::from_secret(data.env.token_secret.as_ref()), + ) + .map_err(|(status, json_err)| { + let error = json!({ + "status": json_err.status, + "message": json_err.message + }); + (status, Json(error)) + })?; + + let uuid = (&claims.sub).parse::().map_err(|_| { + let error = json!({ + "status": "error", + "message": "Invalid user id" + }); + (StatusCode::UNAUTHORIZED, Json(error)) + })?; + + let user = sqlx::query_as::<_, User>(r#"SELECT * FROM users WHERE id = $1"#) + .bind(uuid) + .fetch_optional(&data.db) + .await + .map_err(|e| { + let error = json!({ + "status": "error", + "message": format!("Database error: {}", e) + }); + (StatusCode::INTERNAL_SERVER_ERROR, Json(error)) + })?; + + let user = user.ok_or_else(|| { + let error = json!({ + "status": "error", + "message": "Invalid user" + }); + (StatusCode::UNAUTHORIZED, Json(error)) + })?; + + if !user.is_admin { + let error = json!({ + "status": "error", + "message": "Admin access required" + }); + return Err((StatusCode::FORBIDDEN, Json(error))); + } + + request.extensions_mut().insert(filter_user(&user)); + Ok(next.run(request).await) +} diff --git a/backend/src/router.rs b/backend/src/router.rs index 6f98460..3638c56 100644 --- a/backend/src/router.rs +++ b/backend/src/router.rs @@ -7,7 +7,7 @@ use axum::{ use crate::{ AppState, - cookie::validation::validate_token, + cookie::validation::{validate_admin, validate_token}, handlers::{ auth::{ create_user, delete_user, get_current_user, get_user_by_id, get_users, login, logout, @@ -18,9 +18,7 @@ use crate::{ }; pub fn create_router(state: Arc) -> Router { - let protected_routes = Router::new() - .route("/api/tickets", get(get_tickets)) - .route("/api/tickets/create", post(create_ticket)) + let admin_routes = Router::new() .route( "/api/tickets/{id}", get(get_ticket_by_id) @@ -28,13 +26,22 @@ pub fn create_router(state: Arc) -> Router { .patch(edit_ticket), ) .route("/api/register", post(create_user)) - .route("/api/logout", get(logout)) .route("/api/users", get(get_users)) .route("/api/users/current", get(get_current_user)) .route( "/api/users/{id}", get(get_user_by_id).delete(delete_user).patch(update_user), ) + .layer(middleware::from_fn_with_state( + state.clone(), + validate_admin, + )); + + let protected_routes = Router::new() + .merge(admin_routes) + .route("/api/tickets", get(get_tickets)) + .route("/api/tickets/create", post(create_ticket)) + .route("/api/logout", get(logout)) .layer(middleware::from_fn_with_state( state.clone(), validate_token,