feat: implement guard

This commit is contained in:
Maulana Sodiqin
2025-04-02 09:35:37 +07:00
parent 8a661df059
commit de9c5a6dc1
5 changed files with 239 additions and 26 deletions
+2
View File
@@ -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::*;
@@ -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<AppState>,
Query(meta): Query<MetaRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
) -> 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<AppState>,
Json(payload): Json<PermissionsRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
Json(payload): Json<PermissionsRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
) -> 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,
}
}
@@ -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<PermissionsEnum>,
) -> 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<String> =
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(())
}
+57 -7
View File
@@ -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<AppState>,
Query(meta): Query<MetaRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
) -> 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<AppState>,
Json(payload): Json<RolesRequestCreateDto>,
) -> 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<AppState>,
Path(id): Path<String>,
Json(payload): Json<RolesRequestUpdateDto>,
) -> 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<AppState>,
Path(id): Path<String>,
) -> 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,
}
}
+81 -14
View File
@@ -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<AppState>,
Query(meta): Query<MetaRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
) -> 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<AppState>,
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<AppState>,
Json(payload): Json<UsersCreateRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
Json(payload): Json<UsersUpdateRequestDto>,
) -> 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<AppState>,
header: HeaderMap,
Json(payload): Json<UsersUpdateRequestDto>,
) -> 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<AppState>,
axum::extract::Path(id): axum::extract::Path<String>,
Path(id): Path<String>,
Json(payload): Json<UsersActiveInactiveRequestDto>,
) -> 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<AppState>,
Path(id): Path<String>,
) -> 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,
}
}