diff --git a/migration/src/m20220101_000001_create_table.rs b/migration/src/m20220101_000001_create_table.rs index 0f3dd0c..f7cf460 100644 --- a/migration/src/m20220101_000001_create_table.rs +++ b/migration/src/m20220101_000001_create_table.rs @@ -7,7 +7,7 @@ pub struct Migration; impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { // --------------------------------------------------------------------- - // Tables principales + // Utilisateurs // --------------------------------------------------------------------- manager @@ -51,6 +51,10 @@ impl MigrationTrait for Migration { ) .await?; + // --------------------------------------------------------------------- + // Serveurs + // --------------------------------------------------------------------- + manager .create_table( Table::create() @@ -83,10 +87,149 @@ impl MigrationTrait for Migration { .default(false), ) .col(ColumnDef::new(Alias::new("owner_id")).uuid().null()) + .foreign_key( + ForeignKey::create() + .name("fk_server_owner") + .from(Alias::new("server"), Alias::new("owner_id")) + .to(Alias::new("user"), Alias::new("id")) + .on_delete(ForeignKeyAction::SetNull), + ) .to_owned(), ) .await?; + manager + .create_table( + Table::create() + .table(Alias::new("server_user")) + .if_not_exists() + .col( + ColumnDef::new(Alias::new("id")) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Alias::new("server_id")).uuid().not_null()) + .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) + .col(ColumnDef::new(Alias::new("username")).string().null()) + .col( + ColumnDef::new(Alias::new("joined_at")) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .col( + ColumnDef::new(Alias::new("updated_at")) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .foreign_key( + ForeignKey::create() + .name("fk_server_user_server") + .from(Alias::new("server_user"), Alias::new("server_id")) + .to(Alias::new("server"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk_server_user_user") + .from(Alias::new("server_user"), Alias::new("user_id")) + .to(Alias::new("user"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .index( + Index::create() + .name("uq_server_user") + .col(Alias::new("server_id")) + .col(Alias::new("user_id")) + .unique(), + ) + .to_owned(), + ) + .await?; + + // --------------------------------------------------------------------- + // Rôles + // --------------------------------------------------------------------- + + manager + .create_table( + Table::create() + .table(Alias::new("role")) + .if_not_exists() + .col( + ColumnDef::new(Alias::new("id")) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Alias::new("server_id")).uuid().not_null()) + .col(ColumnDef::new(Alias::new("name")).string().not_null()) + .col( + ColumnDef::new(Alias::new("is_default")) + .boolean() + .not_null() + .default(false), + ) + .col( + ColumnDef::new(Alias::new("created_at")) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .foreign_key( + ForeignKey::create() + .name("fk_role_server") + .from(Alias::new("role"), Alias::new("server_id")) + .to(Alias::new("server"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .index( + Index::create() + .name("uq_role_server_name") + .col(Alias::new("server_id")) + .col(Alias::new("name")) + .unique(), + ) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Alias::new("role_user")) + .if_not_exists() + .col(ColumnDef::new(Alias::new("role_id")).uuid().not_null()) + .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) + .primary_key( + Index::create() + .col(Alias::new("role_id")) + .col(Alias::new("user_id")), + ) + .foreign_key( + ForeignKey::create() + .name("fk_role_user_role") + .from(Alias::new("role_user"), Alias::new("role_id")) + .to(Alias::new("role"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk_role_user_user") + .from(Alias::new("role_user"), Alias::new("user_id")) + .to(Alias::new("user"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + // --------------------------------------------------------------------- + // Catégories et canaux + // --------------------------------------------------------------------- + manager .create_table( Table::create() @@ -123,8 +266,7 @@ impl MigrationTrait for Migration { .name("fk_category_server") .from(Alias::new("category"), Alias::new("server_id")) .to(Alias::new("server"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade), ) .to_owned(), ) @@ -186,7 +328,61 @@ impl MigrationTrait for Migration { .await?; // --------------------------------------------------------------------- - // Contenu et relations utilisateurs + // Membres des canaux + // --------------------------------------------------------------------- + + manager + .create_table( + Table::create() + .table(Alias::new("channel_user")) + .if_not_exists() + .col( + ColumnDef::new(Alias::new("id")) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Alias::new("channel_id")).uuid().not_null()) + .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) + .col( + ColumnDef::new(Alias::new("role")) + .string() + .not_null() + .default("member"), + ) + .col( + ColumnDef::new(Alias::new("joined_at")) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .foreign_key( + ForeignKey::create() + .name("fk_channel_user_channel") + .from(Alias::new("channel_user"), Alias::new("channel_id")) + .to(Alias::new("channel"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk_channel_user_user") + .from(Alias::new("channel_user"), Alias::new("user_id")) + .to(Alias::new("user"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .index( + Index::create() + .name("uq_channel_user") + .col(Alias::new("channel_id")) + .col(Alias::new("user_id")) + .unique(), + ) + .to_owned(), + ) + .await?; + + // --------------------------------------------------------------------- + // Messages et pièces jointes // --------------------------------------------------------------------- manager @@ -276,10 +472,14 @@ impl MigrationTrait for Migration { ) .await?; + // --------------------------------------------------------------------- + // Permissions + // --------------------------------------------------------------------- + manager .create_table( Table::create() - .table(Alias::new("server_user")) + .table(Alias::new("server_role_permission")) .if_not_exists() .col( ColumnDef::new(Alias::new("id")) @@ -288,34 +488,37 @@ impl MigrationTrait for Migration { .primary_key(), ) .col(ColumnDef::new(Alias::new("server_id")).uuid().not_null()) - .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) - .col(ColumnDef::new(Alias::new("username")).string().null()) + .col(ColumnDef::new(Alias::new("role_id")).uuid().not_null()) .col( - ColumnDef::new(Alias::new("joined_at")) - .timestamp_with_time_zone() + ColumnDef::new(Alias::new("permission")) + .big_integer() .not_null() - .default(Expr::current_timestamp()), - ) - .col( - ColumnDef::new(Alias::new("updated_at")) - .timestamp_with_time_zone() - .not_null() - .default(Expr::current_timestamp()), + .default(0), ) .foreign_key( ForeignKey::create() - .name("fk_server_user_server") - .from(Alias::new("server_user"), Alias::new("server_id")) + .name("fk_server_role_permission_server") + .from( + Alias::new("server_role_permission"), + Alias::new("server_id"), + ) .to(Alias::new("server"), Alias::new("id")) .on_delete(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() - .name("fk_server_user_user") - .from(Alias::new("server_user"), Alias::new("user_id")) - .to(Alias::new("user"), Alias::new("id")) + .name("fk_server_role_permission_role") + .from(Alias::new("server_role_permission"), Alias::new("role_id")) + .to(Alias::new("role"), Alias::new("id")) .on_delete(ForeignKeyAction::Cascade), ) + .index( + Index::create() + .name("uq_server_role_permission") + .col(Alias::new("server_id")) + .col(Alias::new("role_id")) + .unique(), + ) .to_owned(), ) .await?; @@ -323,7 +526,54 @@ impl MigrationTrait for Migration { manager .create_table( Table::create() - .table(Alias::new("channel_user")) + .table(Alias::new("channel_role_permission")) + .if_not_exists() + .col( + ColumnDef::new(Alias::new("id")) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Alias::new("channel_id")).uuid().not_null()) + .col(ColumnDef::new(Alias::new("role_id")).uuid().not_null()) + .col( + ColumnDef::new(Alias::new("permission")) + .big_integer() + .not_null() + .default(0), + ) + .foreign_key( + ForeignKey::create() + .name("fk_channel_role_permission_channel") + .from( + Alias::new("channel_role_permission"), + Alias::new("channel_id"), + ) + .to(Alias::new("channel"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk_channel_role_permission_role") + .from(Alias::new("channel_role_permission"), Alias::new("role_id")) + .to(Alias::new("role"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) + .index( + Index::create() + .name("uq_channel_role_permission") + .col(Alias::new("channel_id")) + .col(Alias::new("role_id")) + .unique(), + ) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Alias::new("channel_user_permission")) .if_not_exists() .col( ColumnDef::new(Alias::new("id")) @@ -334,226 +584,34 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Alias::new("channel_id")).uuid().not_null()) .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) .col( - ColumnDef::new(Alias::new("role")) - .string() + ColumnDef::new(Alias::new("permission")) + .big_integer() .not_null() - .default("member"), - ) - .col( - ColumnDef::new(Alias::new("joined_at")) - .timestamp_with_time_zone() - .not_null() - .default(Expr::current_timestamp()), + .default(0), ) .foreign_key( ForeignKey::create() - .name("fk_channel_user_channel") - .from(Alias::new("channel_user"), Alias::new("channel_id")) + .name("fk_channel_user_permission_channel") + .from( + Alias::new("channel_user_permission"), + Alias::new("channel_id"), + ) .to(Alias::new("channel"), Alias::new("id")) .on_delete(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() - .name("fk_channel_user_user") - .from(Alias::new("channel_user"), Alias::new("user_id")) + .name("fk_channel_user_permission_user") + .from(Alias::new("channel_user_permission"), Alias::new("user_id")) .to(Alias::new("user"), Alias::new("id")) .on_delete(ForeignKeyAction::Cascade), ) - .to_owned(), - ) - .await?; - - // --------------------------------------------------------------------- - // Groupes et attributions - // --------------------------------------------------------------------- - - manager - .create_table( - Table::create() - .table(Alias::new("group")) - .if_not_exists() - .col( - ColumnDef::new(Alias::new("id")) - .uuid() - .not_null() - .primary_key(), - ) - .col(ColumnDef::new(Alias::new("server_id")).uuid().not_null()) - .col(ColumnDef::new(Alias::new("name")).string().not_null()) - .col( - ColumnDef::new(Alias::new("is_default")) - .boolean() - .not_null() - .default(false), - ) - .col( - ColumnDef::new(Alias::new("created_at")) - .timestamp_with_time_zone() - .not_null() - .default(Expr::current_timestamp()), - ) - .foreign_key( - ForeignKey::create() - .name("fk_group_server") - .from(Alias::new("group"), Alias::new("server_id")) - .to(Alias::new("server"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await?; - - manager - .create_table( - Table::create() - .table(Alias::new("group_member")) - .if_not_exists() - .col(ColumnDef::new(Alias::new("group_id")).uuid().not_null()) - .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) - .primary_key( + .index( Index::create() - .col(Alias::new("group_id")) - .col(Alias::new("user_id")), - ) - .foreign_key( - ForeignKey::create() - .name("fk_group_member_group") - .from(Alias::new("group_member"), Alias::new("group_id")) - .to(Alias::new("group"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .name("fk_group_member_user") - .from(Alias::new("group_member"), Alias::new("user_id")) - .to(Alias::new("user"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await?; - - // --------------------------------------------------------------------- - // Permissions sources - // --------------------------------------------------------------------- - - manager - .create_table( - Table::create() - .table(Alias::new("group_permission")) - .if_not_exists() - .col( - ColumnDef::new(Alias::new("id")) - .uuid() - .not_null() - .primary_key(), - ) - .col(ColumnDef::new(Alias::new("group_id")).uuid().not_null()) - .col(ColumnDef::new(Alias::new("server_id")).uuid().not_null()) - .col( - ColumnDef::new(Alias::new("scope_type")) - .integer() - .not_null(), - ) - .col(ColumnDef::new(Alias::new("scope_id")).uuid().not_null()) - .col( - ColumnDef::new(Alias::new("server_permissions")) - .big_integer() - .not_null() - .default(0), - ) - .col( - ColumnDef::new(Alias::new("channel_permissions")) - .big_integer() - .not_null() - .default(0), - ) - .col( - ColumnDef::new(Alias::new("voice_permissions")) - .big_integer() - .not_null() - .default(0), - ) - .col( - ColumnDef::new(Alias::new("created_at")) - .timestamp_with_time_zone() - .not_null() - .default(Expr::current_timestamp()), - ) - .foreign_key( - ForeignKey::create() - .name("fk_group_permission_group") - .from(Alias::new("group_permission"), Alias::new("group_id")) - .to(Alias::new("group"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .name("fk_group_permission_server") - .from(Alias::new("group_permission"), Alias::new("server_id")) - .to(Alias::new("server"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await?; - - manager - .create_table( - Table::create() - .table(Alias::new("user_permission")) - .if_not_exists() - .col( - ColumnDef::new(Alias::new("id")) - .uuid() - .not_null() - .primary_key(), - ) - .col(ColumnDef::new(Alias::new("user_id")).uuid().not_null()) - .col(ColumnDef::new(Alias::new("server_id")).uuid().not_null()) - .col( - ColumnDef::new(Alias::new("scope_type")) - .integer() - .not_null(), - ) - .col(ColumnDef::new(Alias::new("scope_id")).uuid().not_null()) - .col( - ColumnDef::new(Alias::new("server_permissions")) - .big_integer() - .not_null() - .default(0), - ) - .col( - ColumnDef::new(Alias::new("channel_permissions")) - .big_integer() - .not_null() - .default(0), - ) - .col( - ColumnDef::new(Alias::new("voice_permissions")) - .big_integer() - .not_null() - .default(0), - ) - .col( - ColumnDef::new(Alias::new("created_at")) - .timestamp_with_time_zone() - .not_null() - .default(Expr::current_timestamp()), - ) - .foreign_key( - ForeignKey::create() - .name("fk_user_permission_user") - .from(Alias::new("user_permission"), Alias::new("user_id")) - .to(Alias::new("user"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .name("fk_user_permission_server") - .from(Alias::new("user_permission"), Alias::new("server_id")) - .to(Alias::new("server"), Alias::new("id")) - .on_delete(ForeignKeyAction::Cascade), + .name("uq_channel_user_permission") + .col(Alias::new("channel_id")) + .col(Alias::new("user_id")) + .unique(), ) .to_owned(), ) @@ -597,6 +655,7 @@ impl MigrationTrait for Migration { .primary_key( Index::create() .col(Alias::new("user_id")) + .col(Alias::new("server_id")) .col(Alias::new("scope_type")) .col(Alias::new("resource_id")), ) @@ -607,92 +666,13 @@ impl MigrationTrait for Migration { .to(Alias::new("user"), Alias::new("id")) .on_delete(ForeignKeyAction::Cascade), ) - .to_owned(), - ) - .await?; - - // --------------------------------------------------------------------- - // Index - // --------------------------------------------------------------------- - - let indexes = [ - ("idx_category_server_id", "category", "server_id"), - ("idx_channel_server_id", "channel", "server_id"), - ("idx_channel_category_id", "channel", "category_id"), - ("idx_message_channel_id", "message", "channel_id"), - ("idx_message_user_id", "message", "user_id"), - ("idx_attachment_message_id", "attachment", "message_id"), - ("idx_server_user_server_id", "server_user", "server_id"), - ("idx_server_user_user_id", "server_user", "user_id"), - ("idx_channel_user_channel_id", "channel_user", "channel_id"), - ("idx_channel_user_user_id", "channel_user", "user_id"), - ("idx_group_server_id", "group", "server_id"), - ("idx_group_member_user_id", "group_member", "user_id"), - ]; - - for (name, table, column) in indexes { - manager - .create_index( - Index::create() - .name(name) - .table(Alias::new(table)) - .col(Alias::new(column)) - .to_owned(), - ) - .await?; - } - - manager - .create_index( - Index::create() - .name("idx_group_permission_group_scope") - .table(Alias::new("group_permission")) - .col(Alias::new("group_id")) - .col(Alias::new("scope_type")) - .col(Alias::new("scope_id")) - .to_owned(), - ) - .await?; - - manager - .create_index( - Index::create() - .name("idx_group_permission_server") - .table(Alias::new("group_permission")) - .col(Alias::new("server_id")) - .to_owned(), - ) - .await?; - - manager - .create_index( - Index::create() - .name("idx_user_permission_user_scope") - .table(Alias::new("user_permission")) - .col(Alias::new("user_id")) - .col(Alias::new("scope_type")) - .col(Alias::new("scope_id")) - .to_owned(), - ) - .await?; - - manager - .create_index( - Index::create() - .name("idx_user_permission_server") - .table(Alias::new("user_permission")) - .col(Alias::new("server_id")) - .to_owned(), - ) - .await?; - - manager - .create_index( - Index::create() - .name("idx_computed_permission_resource") - .table(Alias::new("computed_permission")) - .col(Alias::new("scope_type")) - .col(Alias::new("resource_id")) + .foreign_key( + ForeignKey::create() + .name("fk_computed_permission_server") + .from(Alias::new("computed_permission"), Alias::new("server_id")) + .to(Alias::new("server"), Alias::new("id")) + .on_delete(ForeignKeyAction::Cascade), + ) .to_owned(), ) .await?; @@ -701,25 +681,32 @@ impl MigrationTrait for Migration { } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + // Les tables dépendantes doivent être supprimées avant leurs parents. let tables = [ "computed_permission", - "user_permission", - "group_permission", - "group_member", - "channel_user", - "server_user", + "channel_user_permission", + "channel_role_permission", + "server_role_permission", "attachment", "message", + "channel_user", "channel", "category", - "group", + "role_user", + "role", + "server_user", "server", "user", ]; for table in tables { manager - .drop_table(Table::drop().table(Alias::new(table)).to_owned()) + .drop_table( + Table::drop() + .table(Alias::new(table)) + .if_exists() + .to_owned(), + ) .await?; } diff --git a/src/models/channel_role_permission.rs b/src/models/channel_role_permission.rs new file mode 100644 index 0000000..1e31c7c --- /dev/null +++ b/src/models/channel_role_permission.rs @@ -0,0 +1,65 @@ +//! SeaORM Entity pour les permissions d'un rôle dans un canal. + +use crate::permissions::ChannelPermission; +use sea_orm::entity::prelude::*; +use sea_orm::prelude::async_trait::async_trait; +use sea_orm::{NotSet, Set}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "channel_role_permission")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + + pub channel_id: Uuid, + + pub role_id: Uuid, + + /// Bitmask des permissions accordées au rôle dans ce canal. + pub permission: i64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::channel::Entity", + from = "Column::ChannelId", + to = "super::channel::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Channel, + + #[sea_orm( + belongs_to = "super::role::Entity", + from = "Column::RoleId", + to = "super::role::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Role, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Role.def() + } +} + +#[async_trait] +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + id: Set(Uuid::new_v4()), + channel_id: NotSet, + role_id: NotSet, + permission: Set(ChannelPermission::empty().bits() as i64), + } + } +} diff --git a/src/models/channel_user_permission.rs b/src/models/channel_user_permission.rs new file mode 100644 index 0000000..0838676 --- /dev/null +++ b/src/models/channel_user_permission.rs @@ -0,0 +1,65 @@ +//! SeaORM Entity pour les permissions directes d'un utilisateur dans un canal. + +use crate::permissions::ChannelPermission; +use sea_orm::entity::prelude::*; +use sea_orm::prelude::async_trait::async_trait; +use sea_orm::{NotSet, Set}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "channel_user_permission")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + + pub channel_id: Uuid, + + pub user_id: Uuid, + + /// Bitmask des permissions accordées directement à l'utilisateur dans ce canal. + pub permission: i64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::channel::Entity", + from = "Column::ChannelId", + to = "super::channel::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Channel, + + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +#[async_trait] +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + id: Set(Uuid::new_v4()), + channel_id: NotSet, + user_id: NotSet, + permission: Set(ChannelPermission::empty().bits() as i64), + } + } +} diff --git a/src/models/computed_permission.rs b/src/models/computed_permission.rs index 833b398..128bb4f 100644 --- a/src/models/computed_permission.rs +++ b/src/models/computed_permission.rs @@ -1,10 +1,12 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 + +use async_trait::async_trait; /// Permet de cache les permissions des utilisateurs pour éviter de recalculer les permissions à chaque fois qu'une requête est faite. use sea_orm::entity::prelude::*; +use sea_orm::{NotSet, Set}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; - #[derive( Debug, Clone, Copy, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, ToSchema, )] @@ -21,6 +23,8 @@ pub enum PermissionScopeType { #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "computed_permission")] pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, /// L'utilisateur à qui appartiennent ces permissions #[sea_orm(primary_key, auto_increment = false)] pub user_id: Uuid, @@ -35,7 +39,6 @@ pub struct Model { /// Cache des permissions serveur (stocké en i64 pour SQL, utilisé en u64) pub server_permissions: i64, pub channel_permissions: i64, - pub voice_permissions: i64, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -56,4 +59,17 @@ impl Related for Entity { } } -impl ActiveModelBehavior for ActiveModel {} +#[async_trait] +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + id: Set(Uuid::new_v4()), + user_id: NotSet, + server_id: NotSet, + scope_type: NotSet, + resource_id: NotSet, + server_permissions: Set(0), + channel_permissions: Set(0), + } + } +} diff --git a/src/models/group_permission.rs b/src/models/group_permission.rs deleted file mode 100644 index 97f1d0a..0000000 --- a/src/models/group_permission.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; -use utoipa::ToSchema; - -#[derive( - Debug, Clone, Copy, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, ToSchema, -)] -#[sea_orm(rs_type = "i32", db_type = "Integer")] -pub enum PermissionScopeType { - #[sea_orm(num_value = 0)] - Server, - #[sea_orm(num_value = 1)] - Category, - #[sea_orm(num_value = 2)] - Channel, -} - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "group_permission")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub id: Uuid, - pub group_id: Uuid, - pub server_id: Uuid, - // L'ID du scope (soit un Uuid d'une Category, soit l'Uuid d'un Channel, soit l'Uuid du Server) - pub scope_type: PermissionScopeType, - pub scope_id: Uuid, - pub server_permissions: i64, - pub channel_permissions: i64, - pub voice_permissions: i64, - pub created_at: DateTimeUtc, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::group::Entity", - from = "Column::GroupId", - to = "super::group::Column::Id", - on_update = "NoAction", - on_delete = "Cascade" - )] - Group, - #[sea_orm( - belongs_to = "super::server::Entity", - from = "Column::ServerId", - to = "super::server::Column::Id", - on_update = "NoAction", - on_delete = "Cascade" - )] - Server, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Group.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Server.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/src/models/mod.rs b/src/models/mod.rs index fbe3d63..ba05fa8 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -5,13 +5,15 @@ pub mod prelude; pub mod attachment; pub mod category; pub mod channel; +pub mod channel_role_permission; pub mod channel_user; +pub mod channel_user_permission; pub mod computed_permission; -pub mod group; -pub mod group_member; -pub mod group_permission; pub mod message; +pub mod role; +pub mod role_user; pub mod server; +pub mod server_role_permission; pub mod server_user; +pub mod server_user_permission; pub mod user; -pub mod user_permission; diff --git a/src/models/prelude.rs b/src/models/prelude.rs index c61eeb1..c997495 100644 --- a/src/models/prelude.rs +++ b/src/models/prelude.rs @@ -5,11 +5,11 @@ pub use super::category::Entity as Category; pub use super::channel::Entity as Channel; pub use super::channel_user::Entity as ChannelUser; pub use super::computed_permission::Entity as ComputedPermission; -pub use super::group::Entity as Group; -pub use super::group_member::Entity as GroupMember; -pub use super::group_permission::Entity as GroupPermission; pub use super::message::Entity as Message; +pub use super::role::Entity as Group; +pub use super::role_user::Entity as GroupMember; pub use super::server::Entity as Server; +pub use super::server_role_permission::Entity as ServerRolePermission; pub use super::server_user::Entity as ServerUser; +pub use super::server_user_permission::Entity as ServerUserPermission; pub use super::user::Entity as User; -pub use super::user_permission::Entity as UserPermission; diff --git a/src/models/group.rs b/src/models/role.rs similarity index 83% rename from src/models/group.rs rename to src/models/role.rs index 00cb6f0..7b5c684 100644 --- a/src/models/group.rs +++ b/src/models/role.rs @@ -5,7 +5,7 @@ use sea_orm::prelude::async_trait::async_trait; use sea_orm::Set; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "group")] +#[sea_orm(table_name = "role")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: Uuid, @@ -25,8 +25,8 @@ pub enum Relation { on_delete = "Cascade" )] Server, - #[sea_orm(has_many = "super::group_member::Entity")] - GroupMember, + #[sea_orm(has_many = "super::role_user::Entity")] + RoleUser, } impl Related for Entity { @@ -35,9 +35,9 @@ impl Related for Entity { } } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::GroupMember.def() + Relation::RoleUser.def() } } diff --git a/src/models/group_member.rs b/src/models/role_user.rs similarity index 79% rename from src/models/group_member.rs rename to src/models/role_user.rs index 847c178..4e80efc 100644 --- a/src/models/group_member.rs +++ b/src/models/role_user.rs @@ -3,10 +3,10 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "group_member")] +#[sea_orm(table_name = "role_user")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] - pub group_id: Uuid, + pub role_id: Uuid, #[sea_orm(primary_key, auto_increment = false)] pub user_id: Uuid, } @@ -14,9 +14,9 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::group::Entity", - from = "Column::GroupId", - to = "super::group::Column::Id", + belongs_to = "super::role::Entity", + from = "Column::RoleId", + to = "super::role::Column::Id", on_update = "NoAction", on_delete = "Cascade" )] @@ -31,7 +31,7 @@ pub enum Relation { User, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { Relation::Group.def() } diff --git a/src/models/server_role_permission.rs b/src/models/server_role_permission.rs new file mode 100644 index 0000000..2294e1c --- /dev/null +++ b/src/models/server_role_permission.rs @@ -0,0 +1,65 @@ +use crate::permissions::ServerPermission; +use sea_orm::entity::prelude::*; +use sea_orm::prelude::async_trait::async_trait; +use sea_orm::{NotSet, Set}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "server_group_permission")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + + #[sea_orm(primary_key, auto_increment = false)] + pub server_id: Uuid, + + #[sea_orm(primary_key, auto_increment = false)] + pub role_id: Uuid, + + /// Bitmask des permissions accordées directement à l'utilisateur. + pub permission: i64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::server::Entity", + from = "Column::ServerId", + to = "super::server::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Server, + + #[sea_orm( + belongs_to = "super::role::Entity", + from = "Column::RoleId", + to = "super::role::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Role, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Server.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Role.def() + } +} + +#[async_trait] +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + id: Set(Uuid::new_v4()), + server_id: NotSet, + role_id: NotSet, + permission: Set(ServerPermission::empty().bits() as i64), + } + } +} diff --git a/src/models/user_permission.rs b/src/models/server_user_permission.rs similarity index 54% rename from src/models/user_permission.rs rename to src/models/server_user_permission.rs index 47cc38f..e39438c 100644 --- a/src/models/user_permission.rs +++ b/src/models/server_user_permission.rs @@ -1,47 +1,26 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +use crate::permissions::ServerPermission; use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; -use utoipa::ToSchema; - -#[derive( - Debug, Clone, Copy, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, ToSchema, -)] -#[sea_orm(rs_type = "i32", db_type = "Integer")] -pub enum PermissionScopeType { - #[sea_orm(num_value = 0)] - Server, - #[sea_orm(num_value = 1)] - Category, - #[sea_orm(num_value = 2)] - Channel, -} +use sea_orm::prelude::async_trait::async_trait; +use sea_orm::{NotSet, Set}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "user_permission")] +#[sea_orm(table_name = "server_user_permission")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: Uuid, - pub user_id: Uuid, + + #[sea_orm(primary_key, auto_increment = false)] pub server_id: Uuid, - // L'ID du scope (soit un Uuid d'une Category, soit l'Uuid d'un Channel, soit l'Uuid du Server) - pub scope_type: PermissionScopeType, - pub scope_id: Uuid, - pub server_permissions: i64, - pub channel_permissions: i64, - pub voice_permissions: i64, - pub created_at: DateTimeUtc, + + #[sea_orm(primary_key, auto_increment = false)] + pub user_id: Uuid, + + /// Bitmask des permissions accordées directement à l'utilisateur. + pub permission: i64, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "super::user::Entity", - from = "Column::UserId", - to = "super::user::Column::Id", - on_update = "NoAction", - on_delete = "Cascade" - )] - User, #[sea_orm( belongs_to = "super::server::Entity", from = "Column::ServerId", @@ -50,12 +29,15 @@ pub enum Relation { on_delete = "Cascade" )] Server, -} -impl Related for Entity { - fn to() -> RelationDef { - Relation::User.def() - } + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, } impl Related for Entity { @@ -64,4 +46,20 @@ impl Related for Entity { } } -impl ActiveModelBehavior for ActiveModel {} +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +#[async_trait] +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + id: Set(Uuid::new_v4()), + server_id: NotSet, + user_id: NotSet, + permission: Set(ServerPermission::empty().bits() as i64), + } + } +} diff --git a/src/permissions.rs b/src/permissions.rs index b5605db..ec74556 100644 --- a/src/permissions.rs +++ b/src/permissions.rs @@ -37,6 +37,7 @@ bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] #[serde(transparent)] pub struct ChannelPermission: u64 { + // Permission communes / texte /// Voir le canal et son contenu. const READ_CHANNEL = 1 << 0; @@ -66,37 +67,16 @@ bitflags! { /// Épingler ou désépingler des messages. const MANAGE_MESSAGES = 1 << 10; - } -} -bitflags! { - /// Permissions applicables aux canaux vocaux. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] - #[serde(transparent)] - pub struct VoicePermission: u64 { - /// Rejoindre un canal vocal. - const JOIN_CHANNEL = 1 << 0; - - /// Parler dans un canal vocal. - const SPEAK = 1 << 1; - - /// Utiliser sa caméra ou partager son écran. - const STREAM = 1 << 2; - - /// Couper son propre microphone. - const MUTE_SELF = 1 << 3; - - /// Couper le microphone d'un autre membre. - const MUTE_OTHERS = 1 << 4; - - /// Déplacer un membre vers un autre canal vocal. - const MOVE_OTHERS = 1 << 6; - - /// Expulser un membre d'un canal vocal. - const DISCONNECT_OTHERS = 1 << 7; - - /// Modifier les paramètres du canal vocal. - const MANAGE_VOICE_CHANNEL = 1 << 8; + // Permissions vocales (30-45 réservées) + const JOIN_VOICE = 1 << 30; + const SPEAK = 1 << 31; + const STREAM = 1 << 32; + const MUTE_SELF = 1 << 33; + const MUTE_OTHERS = 1 << 34; + const MOVE_OTHERS = 1 << 35; + const DISCONNECT_OTHERS = 1 << 36; + const MANAGE_VOICE_CHANNEL = 1 << 37; } } @@ -105,20 +85,11 @@ bitflags! { pub struct PermissionSet { pub server: ServerPermission, pub channel: ChannelPermission, - pub voice: VoicePermission, } impl PermissionSet { - pub const fn new( - server: ServerPermission, - channel: ChannelPermission, - voice: VoicePermission, - ) -> Self { - Self { - server, - channel, - voice, - } + pub const fn new(server: ServerPermission, channel: ChannelPermission) -> Self { + Self { server, channel } } /// Permissions par défaut accordées à un membre standard. @@ -131,15 +102,10 @@ impl PermissionSet { | ChannelPermission::DELETE_OWN_MESSAGE.bits() | ChannelPermission::ADD_REACTIONS.bits(), ), - VoicePermission::from_bits_retain( - VoicePermission::JOIN_CHANNEL.bits() | VoicePermission::SPEAK.bits(), - ), ); /// Vérifie si l'ensemble contient la permission demandée. pub const fn contains(&self, required: Self) -> bool { - self.server.contains(required.server) - && self.channel.contains(required.channel) - && self.voice.contains(required.voice) + self.server.contains(required.server) && self.channel.contains(required.channel) } } diff --git a/src/repositories/computed_permission.rs b/src/repositories/computed_permission.rs index 84da4f9..c47cdac 100644 --- a/src/repositories/computed_permission.rs +++ b/src/repositories/computed_permission.rs @@ -1,18 +1,17 @@ use crate::models::{ - category, channel, computed_permission, group, group_member, group_permission, server, user, - user_permission, + 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::prelude::*; -use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, TransactionTrait}; -use std::collections::HashMap; -use crate::models::channel::ChannelType; -use crate::models::computed_permission::PermissionScopeType; -use crate::permissions::{ChannelPermission, ServerPermission, VoicePermission}; +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, @@ -20,171 +19,178 @@ pub struct ComputedPermissionRepository { impl ComputedPermissionRepository { pub async fn get_all(&self) -> AnyResult> { - let result = computed_permission::Entity::find() + Ok(computed_permission::Entity::find() .all(&self.context.db) - .await?; - Ok(result) + .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<()> { - // 1. Récupérer la structure (tous les salons + catégories du serveur) - // 2. Récupérer les sources de vérité (Groupes de l'user + Perms individuelles) - // 3. Calculer pour l'UUID du serveur (Pseudo-salon pour le Kick/Ban) - // 4. Pour chaque salon, calculer la fusion des bits - // 5. Tout enregistrer en une seule transaction dans `computed_permission` + // --------------------------------------------------------------------- + // Rôles de l'utilisateur + // --------------------------------------------------------------------- - // Récupération du serveur - let server = server::Entity::find_by_id(server_id) - .one(&self.context.db) - .await?; - - // Récupération de l'utilisateur - let user = user::Entity::find_by_id(user_id) - .one(&self.context.db) - .await?; - - // Récupération des groupes de l'utilisateur - let groups = group::Entity::find() - .filter(group::Column::ServerId.eq(server_id)) - .filter(group_member::Column::UserId.eq(user_id)) + 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?; - // Récupération de toutes les catégories du serveur - let categories = category::Entity::find() - .filter(category::Column::ServerId.eq(server_id)) - .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 + // --------------------------------------------------------------------- - // Récupération de toutes les channels du serveur let channels = channel::Entity::find() .filter(channel::Column::ServerId.eq(server_id)) .all(&self.context.db) .await?; - // retrieve permissions - let user_permissions = user_permission::Entity::find() - .filter(user_permission::Column::UserId.eq(user_id)) - .filter(user_permission::Column::ServerId.eq(server_id)) - .all(&self.context.db) - .await?; + let channel_ids: Vec = channels.iter().map(|channel| channel.id).collect(); - let group_permissions = group_permission::Entity::find() - .filter(group_permission::Column::ServerId.eq(server_id)) - .filter(group_member::Column::UserId.eq(user_id)) - .all(&self.context.db) - .await?; + // --------------------------------------------------------------------- + // Permissions de rôles pour tous les canaux + // --------------------------------------------------------------------- - // computed existing permissions - struct VirtualComputedPermission { - server_permissions: i64, - channel_permissions: i64, - voice_permissions: i64, - } - let mut all_permissions: HashMap> = HashMap::new(); + 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? + }; - user_permissions.into_iter().for_each(|user_permission| { - let permission = VirtualComputedPermission { - server_permissions: user_permission.server_permissions, - channel_permissions: user_permission.channel_permissions, - voice_permissions: user_permission.voice_permissions, - }; + let mut permissions_by_channel: HashMap = HashMap::new(); - all_permissions - .entry(user_permission.scope_id) // vérifier que la clé existe dans la HashMap - .or_default() // appel Vec::new() automatiquement - .push(permission); // insert la permission dans le Vec - }); - - group_permissions.into_iter().for_each(|group_permission| { - let permission = VirtualComputedPermission { - server_permissions: group_permission.server_permissions, - channel_permissions: group_permission.channel_permissions, - voice_permissions: group_permission.voice_permissions, - }; - - all_permissions - .entry(group_permission.scope_id) + for permission in role_channel_permissions { + permissions_by_channel + .entry(permission.channel_id) .or_default() - .push(permission); - }); - - // Compute permissions - let mut computed_permissions: Vec = Vec::new(); - // server - let mut server_permissions = ServerPermission::empty(); - match all_permissions.get(&server_id) { - Some(perm) => { - for permission in perm { - server_permissions |= - ServerPermission::from_bits_retain(permission.server_permissions as u64); - } - - 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), - channel_permissions: Set(ChannelPermission::empty().bits() as i64), - voice_permissions: Set(ChannelPermission::empty().bits() as i64), - }); - } - None => {} + .insert(ChannelPermission::from_bits_retain( + permission.permission as u64, + )); } - // Category - // todo : à faire + // --------------------------------------------------------------------- + // 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); - // channels - channels.into_iter().for_each(|channel| { - let mut channel_permissions = ChannelPermission::empty(); - let mut voice_permissions = VoicePermission::empty(); - match all_permissions.get(&channel.id) { - Some(perm) => { - for permission in perm { - channel_permissions |= ChannelPermission::from_bits_retain( - permission.channel_permissions as u64, - ); - if channel.channel_type == ChannelType::Voice { - voice_permissions |= VoicePermission::from_bits_retain( - permission.voice_permissions as u64, - ); - } - } - } - None => {} - } 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), - server_permissions: Set(ServerPermission::empty().bits() as i64), channel_permissions: Set(channel_permissions.bits() as i64), - voice_permissions: Set(voice_permissions.bits() as i64), + ..Default::default() }); - }); + } + + // --------------------------------------------------------------------- + // Remplacement atomique du cache + // --------------------------------------------------------------------- - // apply in the DB self.context .db - .transaction::<_, (), anyhow::Error>(|txn| { + .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(txn) + .exec(transaction) .await?; + if !computed_permissions.is_empty() { computed_permission::Entity::insert_many(computed_permissions) - .exec(txn) + .exec(transaction) .await?; } + Ok(()) }) }) diff --git a/src/repositories/group.rs b/src/repositories/group.rs index bcd1937..2b9a3e4 100644 --- a/src/repositories/group.rs +++ b/src/repositories/group.rs @@ -1,4 +1,4 @@ -use crate::models::group; +use crate::models::role; use crate::repositories::{AnyResult, RepositoryContext}; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter}; use std::sync::Arc; @@ -10,35 +10,35 @@ pub struct GroupRepository { } impl GroupRepository { - pub async fn get_all_by_server(&self, server_id: Uuid) -> AnyResult> { - Ok(group::Entity::find() - .filter(group::Column::ServerId.eq(server_id)) + pub async fn get_all_by_server(&self, server_id: Uuid) -> AnyResult> { + Ok(role::Entity::find() + .filter(role::Column::ServerId.eq(server_id)) .all(&self.context.db) .await?) } - pub async fn get_all(&self) -> AnyResult> { - Ok(group::Entity::find().all(&self.context.db).await?) + pub async fn get_all(&self) -> AnyResult> { + Ok(role::Entity::find().all(&self.context.db).await?) } - pub async fn get_by_id(&self, id: Uuid) -> AnyResult> { - Ok(group::Entity::find_by_id(id).one(&self.context.db).await?) + pub async fn get_by_id(&self, id: Uuid) -> AnyResult> { + Ok(role::Entity::find_by_id(id).one(&self.context.db).await?) } - pub async fn create(&self, active: group::ActiveModel) -> AnyResult { + pub async fn create(&self, active: role::ActiveModel) -> AnyResult { let group = active.insert(&self.context.db).await?; self.context.events.emit("group_created", group.clone()); Ok(group) } - pub async fn update(&self, active: group::ActiveModel) -> AnyResult { + pub async fn update(&self, active: role::ActiveModel) -> AnyResult { let group = active.update(&self.context.db).await?; self.context.events.emit("group_updated", group.clone()); Ok(group) } pub async fn delete(&self, id: Uuid) -> AnyResult { - let res = group::Entity::delete_by_id(id) + let res = role::Entity::delete_by_id(id) .exec(&self.context.db) .await?; self.context.events.emit("group_deleted", id); diff --git a/src/repositories/server.rs b/src/repositories/server.rs index 7f8a30c..64c2e82 100644 --- a/src/repositories/server.rs +++ b/src/repositories/server.rs @@ -1,6 +1,6 @@ use super::types::{ServerExplorerItem, ServerTree}; use super::{AnyResult, RepositoryContext}; -use crate::models::{category, channel, group, server, server_user}; +use crate::models::{category, channel, role, server, server_user}; use sea_orm::prelude::*; use sea_orm::{ActiveModelTrait, Set}; @@ -38,7 +38,7 @@ impl ServerRepository { let server = active.insert(&self.context.db).await?; // Créer le groupe par défaut pour le serveur - let default_group = group::ActiveModel { + let default_group = role::ActiveModel { server_id: Set(server.id), name: Set("Member".to_string()), is_default: Set(true), diff --git a/src/routes/group/mapper.rs b/src/routes/group/mapper.rs index 6113250..96c4904 100644 --- a/src/routes/group/mapper.rs +++ b/src/routes/group/mapper.rs @@ -1,9 +1,9 @@ -use crate::models::group; +use crate::models::role; use crate::routes::group::dto::{CreateGroupRequest, GroupResponse, UpdateGroupRequest}; use sea_orm::Set; use uuid::Uuid; -pub fn group_model_to_group_response(model: group::Model) -> GroupResponse { +pub fn group_model_to_group_response(model: role::Model) -> GroupResponse { GroupResponse { id: model.id, server_id: model.server_id, @@ -13,8 +13,8 @@ pub fn group_model_to_group_response(model: group::Model) -> GroupResponse { } } -pub fn create_request_to_am(req: CreateGroupRequest) -> group::ActiveModel { - group::ActiveModel { +pub fn create_request_to_am(req: CreateGroupRequest) -> role::ActiveModel { + role::ActiveModel { id: Set(Uuid::new_v4()), server_id: Set(req.server_id), name: Set(req.name), @@ -27,8 +27,8 @@ pub fn update_request_to_am( id: Uuid, server_id: Uuid, req: UpdateGroupRequest, -) -> group::ActiveModel { - group::ActiveModel { +) -> role::ActiveModel { + role::ActiveModel { id: Set(id), server_id: Set(server_id), name: Set(req.name),