From 75bc9d2440985be937b43ac315d0fe3685321750 Mon Sep 17 00:00:00 2001 From: Nell Date: Sat, 11 Jul 2026 03:11:41 +0200 Subject: [PATCH] init --- .junie/perm.md | 533 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 .junie/perm.md diff --git a/.junie/perm.md b/.junie/perm.md new file mode 100644 index 0000000..4678e73 --- /dev/null +++ b/.junie/perm.md @@ -0,0 +1,533 @@ +Oui. Si tu gardes les bitflags, je simplifierais tes modèles autour de cette règle : + +> Les permissions appartiennent aux **groupes** ou sont accordées directement à un **membre**, et chaque attribution +> possède un périmètre. + +Je ne mettrais plus les permissions par défaut directement sur `server` et `channel`. + +## 1. `user` + +Je garderais quasiment ton modèle actuel : + +```plain text +user +---- +id +username +password +pub_key +created_at +updated_at +is_superuser +``` + +`is_superuser` reste un bypass global, indépendant des permissions d’un serveur. + +Je ne mettrais pas de permissions directement dans `user`, car les permissions doivent être contextuelles à un serveur. + +## 2. `server` + +Je supprimerais : + +```plain text +default_server_permissions +default_channel_permissions +default_voice_permissions +``` + +Le serveur ne devrait pas porter directement les permissions. Il possède plutôt des groupes. + +```plain text +server +------ +id +name +password +created_at +updated_at +is_default +``` + +Les permissions par défaut seraient celles du groupe système `Everyone`. + +## 3. `server_user` + +Je garderais cette table pour l’appartenance d’un utilisateur à un serveur, mais je supprimerais probablement : + +```plain text +is_admin +is_owner +server_permissions +channel_permissions +voice_permissions +``` + +Je remplacerais les statuts par des groupes : + +```plain text +server_user +----------- +id +server_id +user_id +username +joined_at +updated_at +``` + +Puis : + +```plain text +group_member +------------ +group_id +user_id +``` + +Par exemple : + +```plain text +Everyone +Membre +Modérateur +Administrateur +``` + +L’utilisateur propriétaire pourrait rester une exception structurelle : + +```plain text +server.owner_id +``` + +ou être représenté par un groupe système très privilégié. Personnellement, je conserverais `owner_id` pour éviter qu’un +propriétaire perde accidentellement ses droits. + +## 4. `group` + +Ta table actuelle est déjà proche de ce qu’il faut : + +```plain text +group +----- +id +server_id +name +is_default +created_at +updated_at +``` + +Je garderais les trois bitmasks : + +```plain text +server_permissions +channel_permissions +voice_permissions +``` + +Donc : + +```rust +pub struct Model { + pub id: Uuid, + pub server_id: Uuid, + pub name: String, + pub is_default: bool, + pub server_permissions: i64, + pub channel_permissions: i64, + pub voice_permissions: i64, + pub created_at: DateTimeUtc, +} +``` + +Chaque groupe est alors un rôle contenant un ensemble de permissions. + +Exemples : + +```plain text +Everyone : + READ_CHANNEL + SEND_MESSAGE + JOIN_CHANNEL + SPEAK + +Modérateur : + DELETE_OTHERS_MESSAGES + MANAGE_MESSAGES + MUTE_OTHERS +``` + +## 5. `group_member` + +C’est ici que j’ajouterais le périmètre. + +Actuellement, un groupe est uniquement attribué à un utilisateur de manière globale au serveur : + +```plain text +group_id +user_id +``` + +Pour permettre à un modérateur d’agir seulement dans une catégorie ou un canal, il faut ajouter un scope. + +### Option que je recommande + +```plain text +group_member +------------ +group_id +user_id +scope_type +scope_id +``` + +Exemples : + +```plain text +Modérateur -> Alice -> server -> serveur A +Modérateur -> Bob -> category -> catégorie Support +Modérateur -> Claire -> channel -> canal Général +``` + +Avec un enum : + +```rust +pub enum PermissionScopeType { + Server, + Category, + Channel, +} +``` + +Le problème est que `scope_id` peut pointer vers plusieurs tables. Il faudra donc valider la cohérence côté application, +ou utiliser trois colonnes nullable. + +### Variante plus propre SQL + +```plain text +group_member +------------ +group_id +user_id +server_id +category_id +channel_id +``` + +Avec la règle : + +```plain text +exactement une portée est définie +``` + +Mais cette variante est plus lourde à manipuler. + +Pour un projet avec SeaORM, je choisirais probablement `scope_type + scope_id`, avec validation dans le service de +permissions. + +## 6. Permissions individuelles + +Tu as deux possibilités. + +### Option simple : les stocker dans `server_user` + +Pour une permission individuelle valable sur tout le serveur : + +```plain text +server_user +----------- +server_permissions +channel_permissions +voice_permissions +``` + +Cela permet : + +```plain text +Alice possède individuellement SEND_MESSAGE sur le serveur +``` + +Mais cette solution ne permet pas facilement une permission individuelle limitée à une catégorie ou un canal. + +### Option plus flexible : créer `user_permission_scope` + +Je recommande cette table : + +```plain text +user_permission +--------------- +id +user_id +server_id +category_id nullable +channel_id nullable +server_permissions +channel_permissions +voice_permissions +created_at +``` + +Exemples : + +```plain text +Alice -> SEND_MESSAGE -> serveur A +Bob -> DELETE_OTHERS_MESSAGES -> catégorie Support +Claire -> SEND_MESSAGE -> canal Général +``` + +La colonne `server_id` est utile pour garantir que toutes les ressources appartiennent au bon serveur. + +## 7. Que faire de `channel_user` ? + +Ta table actuelle contient : + +```plain text +channel_user +------------ +channel_id +user_id +role +permissions +``` + +Je ne garderais pas `role` et `permissions` sous cette forme si tu as déjà `group` et `group_member`. + +Je l’utiliserais uniquement pour représenter une relation particulière au canal, par exemple : + +```plain text +channel_user +------------ +id +channel_id +user_id +joined_at +``` + +Ou je la supprimerais complètement si l’accès au canal est déduit des permissions et de l’appartenance au serveur. + +Pour les permissions, je préfère `user_permission`, qui couvre déjà : + +- serveur ; +- catégorie ; +- canal. + +Sinon tu risques d’avoir trois systèmes concurrents : + +```plain text +server_user.permissions +channel_user.permissions +group.permissions +``` + +et le calcul deviendra difficile à maintenir. + +## 8. Que faire des permissions de `channel` ? + +Je supprimerais : + +```plain text +default_channel_permissions +default_voice_permissions +``` + +Le rôle par défaut `Everyone` du serveur les remplace. + +Si tu veux qu’un canal accorde des permissions supplémentaires, tu peux utiliser une table dédiée : + +```plain text +channel_permission_grant +------------------------ +channel_id +group_id nullable +user_id nullable +server_permissions +channel_permissions +voice_permissions +``` + +Mais je ne l’ajouterais pas immédiatement. Commence avec : + +```plain text +permissions serveur +permissions de groupe +permissions individuelles +``` + +Puis ajoute les overrides de canal uniquement si le besoin apparaît réellement. + +## Structure que je choisirais maintenant + +```plain text +user +server +category +channel + +server_user +group +group_member +user_permission +``` + +### `server_user` + +Appartenance au serveur : + +```plain text +user_id +server_id +joined_at +``` + +### `group` + +Permissions d’un rôle : + +```plain text +id +server_id +name +is_default +server_permissions +channel_permissions +voice_permissions +``` + +### `group_member` + +Attribution d’un rôle avec périmètre : + +```plain text +group_id +user_id +scope_type +scope_id +``` + +### `user_permission` + +Permission individuelle avec périmètre : + +```plain text +id +user_id +server_id +scope_type +scope_id +server_permissions +channel_permissions +voice_permissions +``` + +## Calcul des permissions + +Pour un utilisateur et un canal donné : + +```plain text +permissions effectives = + permissions des groupes applicables + | permissions individuelles applicables +``` + +Les groupes applicables sont ceux dont le scope est : + +```plain text +serveur parent +catégorie parente +canal courant +``` + +Exemple : + +```plain text +Alice + Everyone sur le serveur + Modérateur sur la catégorie Support + permission individuelle sur le canal Général +``` + +Dans un canal de `Support`, elle reçoit : + +```plain text +permissions Everyone +| permissions Modérateur +| permissions individuelles du canal +``` + +## Problème important avec `is_admin` + +Je supprimerais `is_admin` de `server_user`. + +Un administrateur devient simplement un groupe : + +```plain text +Administrateur +``` + +avec les permissions correspondantes. + +En revanche, je garderais probablement : + +```plain text +server.owner_id +``` + +car le propriétaire est une relation métier particulière, pas seulement un ensemble de permissions. + +## À propos de `i64` + +Puisque tu stockes des valeurs `u64` dans des colonnes `i64`, je ferais au minimum une abstraction claire côté Rust : + +```rust +fn permissions_from_db(value: i64) -> u64 { + value as u64 +} +``` + +Et j’éviterais de disperser les conversions dans les modèles. + +Si tu ne comptes pas utiliser le bit de poids fort, tu peux aussi décider officiellement que seuls les 63 premiers bits +sont valides. + +## Recommandation finale + +Je modifierais ton système comme ceci : + +```plain text +Supprimer : + permissions de server + permissions de channel + is_admin de server_user + permissions de channel_user + role texte de channel_user + +Garder : + is_superuser sur user + owner_id sur server + trois bitflags sur group + +Ajouter : + scope aux attributions de groupes + table user_permission pour les permissions individuelles +``` + +Le modèle deviendrait : + +```plain text +User + ├── appartient à des serveurs + ├── reçoit des groupes + └── peut recevoir des permissions individuelles + +Group + └── possède trois bitflags + +GroupMember + └── associe un groupe à un utilisateur dans un scope + +UserPermission + └── associe directement des bitflags à un utilisateur dans un scope +``` + +C’est le compromis que je choisirais : **bitflags conservés, groupes réutilisables, permissions individuelles possibles, +et modération limitée par scope**. \ No newline at end of file