init
This commit is contained in:
+533
@@ -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**.
|
||||
Reference in New Issue
Block a user