diff --git a/src/apps/v1/permissions/mod.rs b/src/apps/v1/permissions/mod.rs index 8a6ff77..bb6d01b 100644 --- a/src/apps/v1/permissions/mod.rs +++ b/src/apps/v1/permissions/mod.rs @@ -5,6 +5,7 @@ use axum::{ pub mod permissions_controller; pub mod permissions_dto; pub mod permissions_enum; +pub mod permissions_guard; pub mod permissions_repository; pub mod permissions_schema; pub mod permissions_service; @@ -17,6 +18,7 @@ pub mod permissions_repository_test; pub use permissions_controller::*; pub use permissions_dto::*; pub use permissions_enum::*; +pub use permissions_guard::*; pub use permissions_repository::*; pub use permissions_schema::*; diff --git a/src/apps/v1/permissions/permissions_controller.rs b/src/apps/v1/permissions/permissions_controller.rs index 0e031b6..6cac43c 100644 --- a/src/apps/v1/permissions/permissions_controller.rs +++ b/src/apps/v1/permissions/permissions_controller.rs @@ -13,6 +13,8 @@ use crate::{ ResponseSuccessDto, }; +use super::{permissions_guard, PermissionsEnum}; + #[utoipa::path( get, path = "/v1/permissions", @@ -31,10 +33,20 @@ use crate::{ tag = "Permissions" )] pub async fn get_permission_list( + headers: axum::http::HeaderMap, Extension(state): Extension, Query(meta): Query, ) -> impl IntoResponse { - PermissionsService::get_permission_list(&state, meta).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::ReadListPermissions], + ) + .await + { + Ok(_) => PermissionsService::get_permission_list(&state, meta).await, + Err(response) => response, + } } #[utoipa::path( @@ -50,10 +62,20 @@ pub async fn get_permission_list( tag = "Permissions" )] pub async fn get_permission_by_id( + headers: axum::http::HeaderMap, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { - PermissionsService::get_permission_by_id(&state, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::ReadDetailPermissions], + ) + .await + { + Ok(_) => PermissionsService::get_permission_by_id(&state, id).await, + Err(response) => response, + } } #[utoipa::path( @@ -69,10 +91,20 @@ pub async fn get_permission_by_id( tag = "Permissions" )] pub async fn post_create_permission( + headers: axum::http::HeaderMap, Extension(state): Extension, Json(payload): Json, ) -> impl IntoResponse { - PermissionsService::create_role(&state, payload).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::CreatePermissions], + ) + .await + { + Ok(_) => PermissionsService::create_role(&state, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -88,11 +120,21 @@ pub async fn post_create_permission( tag = "Permissions" )] pub async fn put_update_permission( + headers: axum::http::HeaderMap, Extension(state): Extension, Path(id): Path, Json(payload): Json, ) -> impl IntoResponse { - PermissionsService::update_permission(&state, payload, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::UpdatePermissions], + ) + .await + { + Ok(_) => PermissionsService::update_permission(&state, payload, id).await, + Err(response) => response, + } } #[utoipa::path( @@ -107,8 +149,18 @@ pub async fn put_update_permission( tag = "Permissions" )] pub async fn delete_permission( + headers: axum::http::HeaderMap, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { - PermissionsService::delete_permission(&state, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::DeletePermissions], + ) + .await + { + Ok(_) => PermissionsService::delete_permission(&state, id).await, + Err(response) => response, + } } diff --git a/src/apps/v1/permissions/permissions_guard.rs b/src/apps/v1/permissions/permissions_guard.rs new file mode 100644 index 0000000..e04d513 --- /dev/null +++ b/src/apps/v1/permissions/permissions_guard.rs @@ -0,0 +1,42 @@ +use super::PermissionsEnum; +use crate::{common_response, extract_email, AppState, AuthRepository}; +use axum::{ + http::{HeaderMap, StatusCode}, + response::Response, +}; + +pub async fn permissions_guard( + headers: &HeaderMap, + state: AppState, + required_permissions: Vec, +) -> Result<(), Response> { + let auth_repo = AuthRepository::new(&state); + let email = extract_email(headers).ok_or_else(|| { + common_response( + StatusCode::UNAUTHORIZED, + "Invalid or missing authorization token", + ) + })?; + let raw_user = auth_repo + .query_get_stored_user(email.clone()) + .await + .map_err(|_| { + common_response( + StatusCode::UNAUTHORIZED, + "User session expired or not found", + ) + })?; + let role = raw_user.role; + let role_permissions: Vec = + role.permissions.into_iter().map(|perm| perm.name).collect(); + let has_all_permissions = required_permissions + .iter() + .all(|required| role_permissions.contains(&required.to_string())); + if !has_all_permissions { + return Err(common_response( + StatusCode::FORBIDDEN, + "You don't have the required permissions", + )); + } + Ok(()) +} diff --git a/src/apps/v1/roles/roles_controller.rs b/src/apps/v1/roles/roles_controller.rs index b5d23cc..ecb8151 100644 --- a/src/apps/v1/roles/roles_controller.rs +++ b/src/apps/v1/roles/roles_controller.rs @@ -6,8 +6,8 @@ use axum::{ use super::{RolesItemDto, RolesRequestCreateDto, RolesRequestUpdateDto}; use crate::{ - v1::roles_service::RolesService, AppState, MessageResponseDto, MetaRequestDto, - ResponseListSuccessDto, ResponseSuccessDto, + permissions_guard, v1::roles_service::RolesService, AppState, MessageResponseDto, + MetaRequestDto, PermissionsEnum, ResponseListSuccessDto, ResponseSuccessDto, }; #[utoipa::path( @@ -31,10 +31,20 @@ use crate::{ tag = "Roles" )] pub async fn get_role_list( + headers: axum::http::HeaderMap, Extension(state): Extension, Query(meta): Query, ) -> impl IntoResponse { - RolesService::get_role_list(&state, meta).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::ReadListRoles], + ) + .await + { + Ok(_) => RolesService::get_role_list(&state, meta).await, + Err(response) => response, + } } #[utoipa::path( @@ -50,10 +60,20 @@ pub async fn get_role_list( tag = "Roles" )] pub async fn get_role_by_id( + headers: axum::http::HeaderMap, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { - RolesService::get_role_by_id(&state, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::ReadDetailRoles], + ) + .await + { + Ok(_) => RolesService::get_role_by_id(&state, id).await, + Err(response) => response, + } } #[utoipa::path( @@ -69,10 +89,20 @@ pub async fn get_role_by_id( tag = "Roles" )] pub async fn post_create_role( + headers: axum::http::HeaderMap, Extension(state): Extension, Json(payload): Json, ) -> impl IntoResponse { - RolesService::create_role(&state, payload).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::CreateRoles], + ) + .await + { + Ok(_) => RolesService::create_role(&state, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -88,11 +118,21 @@ pub async fn post_create_role( tag = "Roles" )] pub async fn put_update_role( + headers: axum::http::HeaderMap, Extension(state): Extension, Path(id): Path, Json(payload): Json, ) -> impl IntoResponse { - RolesService::update_role(&state, id, payload).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::UpdateRoles], + ) + .await + { + Ok(_) => RolesService::update_role(&state, id, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -107,8 +147,18 @@ pub async fn put_update_role( tag = "Roles" )] pub async fn delete_role( + headers: axum::http::HeaderMap, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { - RolesService::delete_role(&state, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::DeleteRoles], + ) + .await + { + Ok(_) => RolesService::delete_role(&state, id).await, + Err(response) => response, + } } diff --git a/src/apps/v1/users/users_controller.rs b/src/apps/v1/users/users_controller.rs index 4b2ec71..51784ea 100644 --- a/src/apps/v1/users/users_controller.rs +++ b/src/apps/v1/users/users_controller.rs @@ -3,11 +3,12 @@ use axum::http::HeaderMap; use axum::response::IntoResponse; use axum::{Extension, Json}; -use crate::{v1::users_service::UsersService, AppState, MetaRequestDto}; use crate::{ - MessageResponseDto, ResponseListSuccessDto, ResponseSuccessDto, - UsersActiveInactiveRequestDto, UsersCreateRequestDto, UsersDetailItemDto, + permissions_guard, MessageResponseDto, PermissionsEnum, ResponseListSuccessDto, + ResponseSuccessDto, UsersActiveInactiveRequestDto, UsersCreateRequestDto, + UsersDetailItemDto, }; +use crate::{v1::users_service::UsersService, AppState, MetaRequestDto}; use super::{UsersListItemDto, UsersUpdateRequestDto}; @@ -32,10 +33,20 @@ use super::{UsersListItemDto, UsersUpdateRequestDto}; tag = "Users" )] pub async fn get_user_list( + headers: HeaderMap, Extension(state): Extension, Query(meta): Query, ) -> impl IntoResponse { - UsersService::get_user_list(&state, meta).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::ReadListUsers], + ) + .await + { + Ok(_) => UsersService::get_user_list(&state, meta).await, + Err(response) => response, + } } #[utoipa::path( @@ -53,10 +64,20 @@ pub async fn get_user_list( tag = "Users" )] pub async fn get_user_by_id( + headers: HeaderMap, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { - UsersService::get_user_by_id(&state, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::ReadDetailUsers], + ) + .await + { + Ok(_) => UsersService::get_user_by_id(&state, id).await, + Err(response) => response, + } } #[utoipa::path( @@ -72,9 +93,12 @@ pub async fn get_user_by_id( )] pub async fn get_user_me( Extension(state): Extension, - header: HeaderMap, + headers: HeaderMap, ) -> impl IntoResponse { - UsersService::get_user_me(header, &state).await + match permissions_guard(&headers, state.clone(), vec![]).await { + Ok(_) => UsersService::get_user_me(headers, &state).await, + Err(response) => response, + } } #[utoipa::path( @@ -90,10 +114,20 @@ pub async fn get_user_me( tag = "Users" )] pub async fn post_create_user( + headers: HeaderMap, Extension(state): Extension, Json(payload): Json, ) -> impl IntoResponse { - UsersService::create_user(&state, payload).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::CreateUsers], + ) + .await + { + Ok(_) => UsersService::create_user(&state, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -109,11 +143,21 @@ pub async fn post_create_user( tag = "Users" )] pub async fn put_update_user( + headers: HeaderMap, Extension(state): Extension, Path(id): Path, Json(payload): Json, ) -> impl IntoResponse { - UsersService::update_user(&state, id, payload).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::UpdateUsers], + ) + .await + { + Ok(_) => UsersService::update_user(&state, id, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -129,11 +173,14 @@ pub async fn put_update_user( tag = "Users" )] pub async fn put_update_user_me( + headers: HeaderMap, Extension(state): Extension, - header: HeaderMap, Json(payload): Json, ) -> impl IntoResponse { - UsersService::update_user_me(&state, header, payload).await + match permissions_guard(&headers, state.clone(), vec![]).await { + Ok(_) => UsersService::update_user_me(&state, headers, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -149,11 +196,21 @@ pub async fn put_update_user_me( tag = "Users" )] pub async fn patch_user_active_status( + headers: HeaderMap, Extension(state): Extension, - axum::extract::Path(id): axum::extract::Path, + Path(id): Path, Json(payload): Json, ) -> impl IntoResponse { - UsersService::set_user_active_status(&state, id, payload).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::UpdateUsers], + ) + .await + { + Ok(_) => UsersService::set_user_active_status(&state, id, payload).await, + Err(response) => response, + } } #[utoipa::path( @@ -168,8 +225,18 @@ pub async fn patch_user_active_status( tag = "Users" )] pub async fn delete_user( + headers: HeaderMap, Extension(state): Extension, Path(id): Path, ) -> impl IntoResponse { - UsersService::delete_user(&state, id).await + match permissions_guard( + &headers, + state.clone(), + vec![PermissionsEnum::DeleteUsers], + ) + .await + { + Ok(_) => UsersService::delete_user(&state, id).await, + Err(response) => response, + } }