role_service.rs
210 lignes · 6552 octets
use crate::error::AppError; use crate::models::{role, user_role}; use chrono::Utc; use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, Set}; use uuid::Uuid; /// Service for managing roles in the RBAC system /// /// This service provides business logic for role operations, /// including creating predefined roles (admin, user). pub struct RoleService; impl RoleService { /// Create a new role with the given name and description /// /// # Arguments /// /// * `db` - Database connection /// * `name` - Role name (e.g., "admin", "user") /// * `description` - Optional description of the role /// /// # Returns /// /// The created role model /// /// # Errors /// /// Returns `AppError::DbErr` if database operation fails /// Returns `AppError::Conflict` if role with same name already exists pub async fn create_role( db: &DatabaseConnection, name: String, description: Option<String>, ) -> Result<role::Model, AppError> { let now = Utc::now(); let role = role::ActiveModel { id: Set(Uuid::new_v4()), name: Set(name), description: Set(description), created_at: Set(now), updated_at: Set(now), }; let result = role.insert(db).await?; Ok(result) } /// Get a role by its name /// /// # Arguments /// /// * `db` - Database connection /// * `name` - Role name to search for /// /// # Returns /// /// The role model if found, None otherwise /// /// # Errors /// /// Returns `AppError::DbErr` if database operation fails pub async fn get_role_by_name( db: &DatabaseConnection, name: &str, ) -> Result<Option<role::Model>, AppError> { use sea_orm::ColumnTrait; use sea_orm::QueryFilter; let role = role::Entity::find() .filter(role::Column::Name.eq(name)) .one(db) .await?; Ok(role) } /// Initialize predefined roles (admin, user, app_manager) /// /// This function creates the default roles if they don't exist. /// It's idempotent - can be called multiple times safely. /// /// # Arguments /// /// * `db` - Database connection /// /// # Returns /// /// A tuple of (admin_role, user_role, app_manager_role) /// /// # Errors /// /// Returns `AppError::Database` if database operation fails pub async fn initialize_default_roles( db: &DatabaseConnection, ) -> Result<(role::Model, role::Model, role::Model), AppError> { // Create admin role if it doesn't exist let admin_role = match Self::get_role_by_name(db, "admin").await? { Some(role) => role, None => { tracing::info!("Creating default 'admin' role"); Self::create_role( db, "admin".to_string(), Some("Administrator with full access".to_string()), ) .await? } }; // Create user role if it doesn't exist let user_role = match Self::get_role_by_name(db, "user").await? { Some(role) => role, None => { tracing::info!("Creating default 'user' role"); Self::create_role( db, "user".to_string(), Some("Standard user with basic access".to_string()), ) .await? } }; // Create app_manager role if it doesn't exist (Feature 013) let app_manager_role = match Self::get_role_by_name(db, "app_manager").await? { Some(role) => role, None => { tracing::info!("Creating default 'app_manager' role"); Self::create_role( db, "app_manager".to_string(), Some("Can manage (enable/disable) applications".to_string()), ) .await? } }; Ok((admin_role, user_role, app_manager_role)) } /// Return all roles stored in the database. pub async fn list_all_roles(db: &DatabaseConnection) -> Result<Vec<role::Model>, AppError> { let roles = role::Entity::find().all(db).await?; Ok(roles) } /// Return a single role by its UUID, or `None` if not found. pub async fn get_role_by_id( db: &DatabaseConnection, id: Uuid, ) -> Result<Option<role::Model>, AppError> { let role = role::Entity::find_by_id(id).one(db).await?; Ok(role) } /// Update the name and/or description of an existing role. pub async fn update_role( db: &DatabaseConnection, id: Uuid, name: Option<String>, description: Option<String>, ) -> Result<role::Model, AppError> { let existing = Self::get_role_by_id(db, id) .await? .ok_or_else(|| AppError::NotFound("Role not found".to_string()))?; let mut active: role::ActiveModel = existing.into(); if let Some(n) = name { active.name = Set(n); } active.description = Set(description); active.updated_at = Set(Utc::now()); let updated = active.update(db).await?; Ok(updated) } /// Delete a role by its UUID. Returns an error if the role is protected (`admin` or `user`). pub async fn delete_role(db: &DatabaseConnection, id: Uuid) -> Result<(), AppError> { let existing = Self::get_role_by_id(db, id) .await? .ok_or_else(|| AppError::NotFound("Role not found".to_string()))?; let protected = ["admin", "user"]; if protected.contains(&existing.name.as_str()) { return Err(AppError::Validation(format!( "Cannot delete protected role '{}'", existing.name ))); } role::Entity::delete_by_id(id).exec(db).await?; Ok(()) } /// Count the number of users assigned to a role. pub async fn count_users_for_role( db: &DatabaseConnection, role_id: Uuid, ) -> Result<u64, AppError> { use sea_orm::PaginatorTrait; let count = user_role::Entity::find() .filter(user_role::Column::RoleId.eq(role_id)) .count(db) .await?; Ok(count) } }
GitRust