feat(auth): add role/permission management (backend + frontend)
- RoleService: CRUD, assign_permissions, get_role_permissions - PermissionService: list all tenant permissions - Role handlers: 8 endpoints with RBAC permission checks - Frontend Roles page: table, create/edit modal, permission assignment - Frontend Roles API: full CRUD + permission operations - Routes registered in AuthModule protected_routes
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
pub mod auth_handler;
|
||||
pub mod role_handler;
|
||||
pub mod user_handler;
|
||||
|
||||
217
crates/erp-auth/src/handler/role_handler.rs
Normal file
217
crates/erp-auth/src/handler/role_handler.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
use axum::Extension;
|
||||
use axum::extract::{FromRef, Path, Query, State};
|
||||
use axum::response::Json;
|
||||
use validator::Validate;
|
||||
|
||||
use erp_core::error::AppError;
|
||||
use erp_core::types::{ApiResponse, PaginatedResponse, Pagination, TenantContext};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::auth_state::AuthState;
|
||||
use crate::dto::{AssignPermissionsReq, CreateRoleReq, PermissionResp, RoleResp, UpdateRoleReq};
|
||||
use crate::middleware::rbac::require_permission;
|
||||
use crate::service::permission_service::PermissionService;
|
||||
use crate::service::role_service::RoleService;
|
||||
|
||||
/// GET /api/v1/roles
|
||||
///
|
||||
/// List roles within the current tenant with pagination.
|
||||
/// Requires the `role.list` permission.
|
||||
pub async fn list_roles<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Query(pagination): Query<Pagination>,
|
||||
) -> Result<Json<ApiResponse<PaginatedResponse<RoleResp>>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.list")?;
|
||||
|
||||
let (roles, total) = RoleService::list(ctx.tenant_id, &pagination, &state.db).await?;
|
||||
|
||||
let page = pagination.page.unwrap_or(1);
|
||||
let page_size = pagination.limit();
|
||||
let total_pages = (total + page_size - 1) / page_size;
|
||||
|
||||
Ok(Json(ApiResponse::ok(PaginatedResponse {
|
||||
data: roles,
|
||||
total,
|
||||
page,
|
||||
page_size,
|
||||
total_pages,
|
||||
})))
|
||||
}
|
||||
|
||||
/// POST /api/v1/roles
|
||||
///
|
||||
/// Create a new role within the current tenant.
|
||||
/// Requires the `role.create` permission.
|
||||
pub async fn create_role<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Json(req): Json<CreateRoleReq>,
|
||||
) -> Result<Json<ApiResponse<RoleResp>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.create")?;
|
||||
|
||||
req.validate()
|
||||
.map_err(|e| AppError::Validation(e.to_string()))?;
|
||||
|
||||
let role = RoleService::create(
|
||||
ctx.tenant_id,
|
||||
ctx.user_id,
|
||||
&req.name,
|
||||
&req.code,
|
||||
&req.description,
|
||||
&state.db,
|
||||
&state.event_bus,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(role)))
|
||||
}
|
||||
|
||||
/// GET /api/v1/roles/:id
|
||||
///
|
||||
/// Fetch a single role by ID within the current tenant.
|
||||
/// Requires the `role.read` permission.
|
||||
pub async fn get_role<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<ApiResponse<RoleResp>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.read")?;
|
||||
|
||||
let role = RoleService::get_by_id(id, ctx.tenant_id, &state.db).await?;
|
||||
Ok(Json(ApiResponse::ok(role)))
|
||||
}
|
||||
|
||||
/// PUT /api/v1/roles/:id
|
||||
///
|
||||
/// Update editable role fields (name, description).
|
||||
/// Requires the `role.update` permission.
|
||||
pub async fn update_role<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(req): Json<UpdateRoleReq>,
|
||||
) -> Result<Json<ApiResponse<RoleResp>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.update")?;
|
||||
|
||||
let role = RoleService::update(
|
||||
id,
|
||||
ctx.tenant_id,
|
||||
ctx.user_id,
|
||||
&req.name,
|
||||
&req.description,
|
||||
&state.db,
|
||||
)
|
||||
.await?;
|
||||
Ok(Json(ApiResponse::ok(role)))
|
||||
}
|
||||
|
||||
/// DELETE /api/v1/roles/:id
|
||||
///
|
||||
/// Soft-delete a role by ID within the current tenant.
|
||||
/// System roles cannot be deleted.
|
||||
/// Requires the `role.delete` permission.
|
||||
pub async fn delete_role<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<ApiResponse<()>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.delete")?;
|
||||
|
||||
RoleService::delete(id, ctx.tenant_id, ctx.user_id, &state.db, &state.event_bus).await?;
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
success: true,
|
||||
data: None,
|
||||
message: Some("角色已删除".to_string()),
|
||||
}))
|
||||
}
|
||||
|
||||
/// POST /api/v1/roles/:id/permissions
|
||||
///
|
||||
/// Replace all permission assignments for a role.
|
||||
/// Requires the `role.update` permission.
|
||||
pub async fn assign_permissions<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(req): Json<AssignPermissionsReq>,
|
||||
) -> Result<Json<ApiResponse<()>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.update")?;
|
||||
|
||||
RoleService::assign_permissions(
|
||||
id,
|
||||
ctx.tenant_id,
|
||||
ctx.user_id,
|
||||
&req.permission_ids,
|
||||
&state.db,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
success: true,
|
||||
data: None,
|
||||
message: Some("权限分配成功".to_string()),
|
||||
}))
|
||||
}
|
||||
|
||||
/// GET /api/v1/roles/:id/permissions
|
||||
///
|
||||
/// Fetch all permissions assigned to a role.
|
||||
/// Requires the `role.read` permission.
|
||||
pub async fn get_role_permissions<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<ApiResponse<Vec<PermissionResp>>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "role.read")?;
|
||||
|
||||
let perms = RoleService::get_role_permissions(id, ctx.tenant_id, &state.db).await?;
|
||||
Ok(Json(ApiResponse::ok(perms)))
|
||||
}
|
||||
|
||||
/// GET /api/v1/permissions
|
||||
///
|
||||
/// List all permissions within the current tenant.
|
||||
/// Requires the `permission.list` permission.
|
||||
pub async fn list_permissions<S>(
|
||||
State(state): State<AuthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
) -> Result<Json<ApiResponse<Vec<PermissionResp>>>, AppError>
|
||||
where
|
||||
AuthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "permission.list")?;
|
||||
|
||||
let perms = PermissionService::list(ctx.tenant_id, &state.db).await?;
|
||||
Ok(Json(ApiResponse::ok(perms)))
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use erp_core::error::AppResult;
|
||||
use erp_core::events::EventBus;
|
||||
use erp_core::module::ErpModule;
|
||||
|
||||
use crate::handler::{auth_handler, user_handler};
|
||||
use crate::handler::{auth_handler, role_handler, user_handler};
|
||||
|
||||
/// Auth module implementing the `ErpModule` trait.
|
||||
///
|
||||
@@ -53,6 +53,25 @@ impl AuthModule {
|
||||
.put(user_handler::update_user)
|
||||
.delete(user_handler::delete_user),
|
||||
)
|
||||
.route(
|
||||
"/roles",
|
||||
axum::routing::get(role_handler::list_roles).post(role_handler::create_role),
|
||||
)
|
||||
.route(
|
||||
"/roles/{id}",
|
||||
axum::routing::get(role_handler::get_role)
|
||||
.put(role_handler::update_role)
|
||||
.delete(role_handler::delete_role),
|
||||
)
|
||||
.route(
|
||||
"/roles/{id}/permissions",
|
||||
axum::routing::get(role_handler::get_role_permissions)
|
||||
.post(role_handler::assign_permissions),
|
||||
)
|
||||
.route(
|
||||
"/permissions",
|
||||
axum::routing::get(role_handler::list_permissions),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
pub mod auth_service;
|
||||
pub mod password;
|
||||
pub mod permission_service;
|
||||
pub mod role_service;
|
||||
pub mod seed;
|
||||
pub mod token_service;
|
||||
pub mod user_service;
|
||||
|
||||
38
crates/erp-auth/src/service/permission_service.rs
Normal file
38
crates/erp-auth/src/service/permission_service.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::PermissionResp;
|
||||
use crate::entity::permission;
|
||||
use crate::error::AuthResult;
|
||||
|
||||
/// Permission read-only service — list permissions within a tenant.
|
||||
///
|
||||
/// Permissions are seeded by the system and not typically created via API.
|
||||
pub struct PermissionService;
|
||||
|
||||
impl PermissionService {
|
||||
/// List all active permissions within a tenant.
|
||||
pub async fn list(
|
||||
tenant_id: Uuid,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AuthResult<Vec<PermissionResp>> {
|
||||
let perms = permission::Entity::find()
|
||||
.filter(permission::Column::TenantId.eq(tenant_id))
|
||||
.filter(permission::Column::DeletedAt.is_null())
|
||||
.all(db)
|
||||
.await
|
||||
.map_err(|e| crate::error::AuthError::Validation(e.to_string()))?;
|
||||
|
||||
Ok(perms
|
||||
.iter()
|
||||
.map(|p| PermissionResp {
|
||||
id: p.id,
|
||||
code: p.code.clone(),
|
||||
name: p.name.clone(),
|
||||
resource: p.resource.clone(),
|
||||
action: p.action.clone(),
|
||||
description: p.description.clone(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
321
crates/erp-auth/src/service/role_service.rs
Normal file
321
crates/erp-auth/src/service/role_service.rs
Normal file
@@ -0,0 +1,321 @@
|
||||
use chrono::Utc;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::{PermissionResp, RoleResp};
|
||||
use crate::entity::{permission, role, role_permission};
|
||||
use crate::error::AuthError;
|
||||
use crate::error::AuthResult;
|
||||
use erp_core::events::EventBus;
|
||||
use erp_core::types::Pagination;
|
||||
|
||||
/// Role CRUD service — create, read, update, soft-delete roles within a tenant,
|
||||
/// and manage role-permission assignments.
|
||||
pub struct RoleService;
|
||||
|
||||
impl RoleService {
|
||||
/// List roles within a tenant with pagination.
|
||||
///
|
||||
/// Returns `(roles, total_count)`.
|
||||
pub async fn list(
|
||||
tenant_id: Uuid,
|
||||
pagination: &Pagination,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AuthResult<(Vec<RoleResp>, u64)> {
|
||||
let paginator = role::Entity::find()
|
||||
.filter(role::Column::TenantId.eq(tenant_id))
|
||||
.filter(role::Column::DeletedAt.is_null())
|
||||
.paginate(db, pagination.limit());
|
||||
|
||||
let total = paginator
|
||||
.num_items()
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
let page_index = pagination.page.unwrap_or(1).saturating_sub(1) as u64;
|
||||
let models = paginator
|
||||
.fetch_page(page_index)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
let resps: Vec<RoleResp> = models
|
||||
.iter()
|
||||
.map(|m| RoleResp {
|
||||
id: m.id,
|
||||
name: m.name.clone(),
|
||||
code: m.code.clone(),
|
||||
description: m.description.clone(),
|
||||
is_system: m.is_system,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok((resps, total))
|
||||
}
|
||||
|
||||
/// Fetch a single role by ID, scoped to the given tenant.
|
||||
pub async fn get_by_id(
|
||||
id: Uuid,
|
||||
tenant_id: Uuid,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AuthResult<RoleResp> {
|
||||
let model = role::Entity::find_by_id(id)
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?
|
||||
.filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
|
||||
.ok_or_else(|| AuthError::Validation("角色不存在".to_string()))?;
|
||||
|
||||
Ok(RoleResp {
|
||||
id: model.id,
|
||||
name: model.name.clone(),
|
||||
code: model.code.clone(),
|
||||
description: model.description.clone(),
|
||||
is_system: model.is_system,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new role within the current tenant.
|
||||
///
|
||||
/// Validates code uniqueness, then inserts the record and publishes
|
||||
/// a `role.created` domain event.
|
||||
pub async fn create(
|
||||
tenant_id: Uuid,
|
||||
operator_id: Uuid,
|
||||
name: &str,
|
||||
code: &str,
|
||||
description: &Option<String>,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
) -> AuthResult<RoleResp> {
|
||||
// Check code uniqueness within tenant
|
||||
let existing = role::Entity::find()
|
||||
.filter(role::Column::TenantId.eq(tenant_id))
|
||||
.filter(role::Column::Code.eq(code))
|
||||
.filter(role::Column::DeletedAt.is_null())
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
if existing.is_some() {
|
||||
return Err(AuthError::Validation("角色编码已存在".to_string()));
|
||||
}
|
||||
|
||||
let now = Utc::now();
|
||||
let id = Uuid::now_v7();
|
||||
let model = role::ActiveModel {
|
||||
id: Set(id),
|
||||
tenant_id: Set(tenant_id),
|
||||
name: Set(name.to_string()),
|
||||
code: Set(code.to_string()),
|
||||
description: Set(description.clone()),
|
||||
is_system: Set(false),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
created_by: Set(operator_id),
|
||||
updated_by: Set(operator_id),
|
||||
deleted_at: Set(None),
|
||||
version: Set(1),
|
||||
};
|
||||
model
|
||||
.insert(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"role.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "role_id": id, "code": code }),
|
||||
));
|
||||
|
||||
Ok(RoleResp {
|
||||
id,
|
||||
name: name.to_string(),
|
||||
code: code.to_string(),
|
||||
description: description.clone(),
|
||||
is_system: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update editable role fields (name and description).
|
||||
///
|
||||
/// Code and is_system cannot be changed after creation.
|
||||
pub async fn update(
|
||||
id: Uuid,
|
||||
tenant_id: Uuid,
|
||||
operator_id: Uuid,
|
||||
name: &Option<String>,
|
||||
description: &Option<String>,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AuthResult<RoleResp> {
|
||||
let model = role::Entity::find_by_id(id)
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?
|
||||
.filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
|
||||
.ok_or_else(|| AuthError::Validation("角色不存在".to_string()))?;
|
||||
|
||||
let mut active: role::ActiveModel = model.into();
|
||||
|
||||
if let Some(name) = name {
|
||||
active.name = Set(name.clone());
|
||||
}
|
||||
if let Some(desc) = description {
|
||||
active.description = Set(Some(desc.clone()));
|
||||
}
|
||||
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
|
||||
let updated = active
|
||||
.update(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
Ok(RoleResp {
|
||||
id: updated.id,
|
||||
name: updated.name.clone(),
|
||||
code: updated.code.clone(),
|
||||
description: updated.description.clone(),
|
||||
is_system: updated.is_system,
|
||||
})
|
||||
}
|
||||
|
||||
/// Soft-delete a role by setting the `deleted_at` timestamp.
|
||||
///
|
||||
/// System roles cannot be deleted.
|
||||
pub async fn delete(
|
||||
id: Uuid,
|
||||
tenant_id: Uuid,
|
||||
operator_id: Uuid,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
) -> AuthResult<()> {
|
||||
let model = role::Entity::find_by_id(id)
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?
|
||||
.filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
|
||||
.ok_or_else(|| AuthError::Validation("角色不存在".to_string()))?;
|
||||
|
||||
if model.is_system {
|
||||
return Err(AuthError::Validation("系统角色不可删除".to_string()));
|
||||
}
|
||||
|
||||
let mut active: role::ActiveModel = model.into();
|
||||
active.deleted_at = Set(Some(Utc::now()));
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
active
|
||||
.update(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"role.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "role_id": id }),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace all permission assignments for a role.
|
||||
///
|
||||
/// Soft-deletes existing assignments and creates new ones.
|
||||
pub async fn assign_permissions(
|
||||
role_id: Uuid,
|
||||
tenant_id: Uuid,
|
||||
operator_id: Uuid,
|
||||
permission_ids: &[Uuid],
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AuthResult<()> {
|
||||
// Verify the role exists and belongs to this tenant
|
||||
let _role = role::Entity::find_by_id(role_id)
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?
|
||||
.filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
|
||||
.ok_or_else(|| AuthError::Validation("角色不存在".to_string()))?;
|
||||
|
||||
// Soft-delete existing role_permission rows
|
||||
let existing = role_permission::Entity::find()
|
||||
.filter(role_permission::Column::RoleId.eq(role_id))
|
||||
.filter(role_permission::Column::TenantId.eq(tenant_id))
|
||||
.filter(role_permission::Column::DeletedAt.is_null())
|
||||
.all(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
let now = Utc::now();
|
||||
for rp in existing {
|
||||
let mut active: role_permission::ActiveModel = rp.into();
|
||||
active.deleted_at = Set(Some(now));
|
||||
active.updated_at = Set(now);
|
||||
active.updated_by = Set(operator_id);
|
||||
active
|
||||
.update(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
}
|
||||
|
||||
// Insert new role_permission rows
|
||||
for perm_id in permission_ids {
|
||||
let rp = role_permission::ActiveModel {
|
||||
role_id: Set(role_id),
|
||||
permission_id: Set(*perm_id),
|
||||
tenant_id: Set(tenant_id),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
created_by: Set(operator_id),
|
||||
updated_by: Set(operator_id),
|
||||
deleted_at: Set(None),
|
||||
version: Set(1),
|
||||
};
|
||||
rp.insert(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fetch all permissions assigned to a role.
|
||||
///
|
||||
/// Resolves through the role_permission join table.
|
||||
pub async fn get_role_permissions(
|
||||
role_id: Uuid,
|
||||
tenant_id: Uuid,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AuthResult<Vec<PermissionResp>> {
|
||||
let rp_rows = role_permission::Entity::find()
|
||||
.filter(role_permission::Column::RoleId.eq(role_id))
|
||||
.filter(role_permission::Column::TenantId.eq(tenant_id))
|
||||
.filter(role_permission::Column::DeletedAt.is_null())
|
||||
.all(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
let perm_ids: Vec<Uuid> = rp_rows.iter().map(|rp| rp.permission_id).collect();
|
||||
if perm_ids.is_empty() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let perms = permission::Entity::find()
|
||||
.filter(permission::Column::Id.is_in(perm_ids))
|
||||
.filter(permission::Column::TenantId.eq(tenant_id))
|
||||
.all(db)
|
||||
.await
|
||||
.map_err(|e| AuthError::Validation(e.to_string()))?;
|
||||
|
||||
Ok(perms
|
||||
.iter()
|
||||
.map(|p| PermissionResp {
|
||||
id: p.id,
|
||||
code: p.code.clone(),
|
||||
name: p.name.clone(),
|
||||
resource: p.resource.clone(),
|
||||
action: p.action.clone(),
|
||||
description: p.description.clone(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user