use crate::models::{ channel, channel_role_permission, channel_user_permission, computed_permission, role_user, server_role_permission, server_user, }; use crate::permissions::{ChannelPermission, ServerPermission}; use crate::repositories::{AnyResult, RepositoryContext}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect, Set, TransactionTrait}; use std::collections::HashMap; use std::sync::Arc; use uuid::Uuid; use crate::models::computed_permission::PermissionScopeType; #[derive(Clone, Debug)] pub struct ComputedPermissionRepository { pub context: Arc, } impl ComputedPermissionRepository { pub async fn get_all(&self) -> AnyResult> { Ok(computed_permission::Entity::find() .all(&self.context.db) .await?) } /// Recalcule le cache de permissions pour tous les utilisateurs du serveur. pub async fn full_sync_server(&self, server_id: Uuid) -> AnyResult<()> { let user_ids = server_user::Entity::find() .filter(server_user::Column::ServerId.eq(server_id)) .select_only() .column(server_user::Column::UserId) .into_tuple::() .all(&self.context.db) .await?; for user_id in user_ids { self.full_sync_user(user_id, server_id).await?; } Ok(()) } /// Recalcule le cache de permissions d'un utilisateur sur un serveur. /// /// Les permissions effectives sont composées de : /// /// - permissions serveur accordées aux rôles de l'utilisateur ; /// - permissions de canal accordées aux rôles de l'utilisateur ; /// - permissions directes de l'utilisateur dans les canaux. pub async fn full_sync_user(&self, user_id: Uuid, server_id: Uuid) -> AnyResult<()> { // --------------------------------------------------------------------- // Rôles de l'utilisateur // --------------------------------------------------------------------- let role_ids = role_user::Entity::find() .filter(role_user::Column::UserId.eq(user_id)) .select_only() .column(role_user::Column::RoleId) .into_tuple::() .all(&self.context.db) .await?; // --------------------------------------------------------------------- // Permissions serveur des rôles // --------------------------------------------------------------------- let mut server_permissions = ServerPermission::empty(); if !role_ids.is_empty() { let role_permissions = server_role_permission::Entity::find() .filter(server_role_permission::Column::ServerId.eq(server_id)) .filter(server_role_permission::Column::RoleId.is_in(role_ids.clone())) .all(&self.context.db) .await?; for permission in role_permissions { server_permissions |= ServerPermission::from_bits_retain(permission.permission as u64); } } // --------------------------------------------------------------------- // Canaux du serveur // --------------------------------------------------------------------- let channels = channel::Entity::find() .filter(channel::Column::ServerId.eq(server_id)) .all(&self.context.db) .await?; let channel_ids: Vec = channels.iter().map(|channel| channel.id).collect(); // --------------------------------------------------------------------- // Permissions de rôles pour tous les canaux // --------------------------------------------------------------------- let role_channel_permissions = if role_ids.is_empty() || channel_ids.is_empty() { Vec::new() } else { channel_role_permission::Entity::find() .filter(channel_role_permission::Column::ChannelId.is_in(channel_ids.clone())) .filter(channel_role_permission::Column::RoleId.is_in(role_ids)) .all(&self.context.db) .await? }; let mut permissions_by_channel: HashMap = HashMap::new(); for permission in role_channel_permissions { permissions_by_channel .entry(permission.channel_id) .or_default() .insert(ChannelPermission::from_bits_retain( permission.permission as u64, )); } // --------------------------------------------------------------------- // Permissions directes de l'utilisateur pour tous les canaux // --------------------------------------------------------------------- let user_channel_permissions = if channel_ids.is_empty() { Vec::new() } else { channel_user_permission::Entity::find() .filter(channel_user_permission::Column::UserId.eq(user_id)) .filter(channel_user_permission::Column::ChannelId.is_in(channel_ids)) .all(&self.context.db) .await? }; for permission in user_channel_permissions { permissions_by_channel .entry(permission.channel_id) .or_default() .insert(ChannelPermission::from_bits_retain( permission.permission as u64, )); } // --------------------------------------------------------------------- // Construction du cache // --------------------------------------------------------------------- let mut computed_permissions = Vec::with_capacity(channels.len().saturating_add(1)); // Permissions au niveau serveur. computed_permissions.push(computed_permission::ActiveModel { user_id: Set(user_id), server_id: Set(server_id), scope_type: Set(PermissionScopeType::Server), resource_id: Set(server_id), server_permissions: Set(server_permissions.bits() as i64), ..Default::default() }); // Permissions au niveau canal. for channel in channels { let channel_permissions = permissions_by_channel .remove(&channel.id) .unwrap_or_else(ChannelPermission::empty); computed_permissions.push(computed_permission::ActiveModel { user_id: Set(user_id), server_id: Set(server_id), scope_type: Set(PermissionScopeType::Channel), resource_id: Set(channel.id), channel_permissions: Set(channel_permissions.bits() as i64), ..Default::default() }); } // --------------------------------------------------------------------- // Remplacement atomique du cache // --------------------------------------------------------------------- self.context .db .transaction::<_, (), anyhow::Error>(|transaction| { Box::pin(async move { computed_permission::Entity::delete_many() .filter(computed_permission::Column::UserId.eq(user_id)) .filter(computed_permission::Column::ServerId.eq(server_id)) .exec(transaction) .await?; if !computed_permissions.is_empty() { computed_permission::Entity::insert_many(computed_permissions) .exec(transaction) .await?; } Ok(()) }) }) .await?; Ok(()) } }