diff --git a/Apps/Droits.md b/Apps/Droits.md index faf41a9..d98c905 100644 --- a/Apps/Droits.md +++ b/Apps/Droits.md @@ -8,73 +8,108 @@ Ce Role est relié a la responsabilité/fonction de la personne dans un Club. Il ## Permission -Une permission est, intuitivement, une formule logique indiquant si un utilisateur a le droit d'intéragir avec un modèle ou non. Cela peut concerner uniquement un champ d'un modèle. +Une permission est un Model Django dont les principaux attributs sont: + * `model` : Le model sur lequel cette permission va s'appliquer + * `type` : Les différents types d'interaction sont : voir (`view`), modifier (`change`), ajouter (`add`) et supprimer (`delete`). + * `query` : Requete sur la cible, encodé en JSON, traduit en un Q object (cf [Query](#compilation-de-la-query) + * `field` : le champ cible qui pourra etre modifié. (tous les champs si vide) -Les différents types d'interaction sont : voir (`view`), modifier (`change`), ajouter (`add`) et supprimer (`delete`). +Pour savoir si un utilisateur a le droit sur un modèle ou non, la requête est compilée (voir ci-dessous) en un fitre de requête dans la base de données, un objet de la classe `Q` (En SQL l'objet Q s'interprete comme tout ce qui suit un `WHERE ...` Ils peuvent être combiné à l'aide d'opérateurs logiques. Plus d'information sur les Q object dans la [documentation officiel](https://docs.djangoproject.com/fr/2.2/topics/db/queries/#complex-lookups-with-q-objects) ) -Pour savoir si un utilisateur a le droit sur un modèle ou non, la requête est compilée (voir ci-dessous) en un fitre de requête dans la base de données, un objet de la classe `Q`. Ensuite, il suffit d'appliquer ce filtre sur une requête à la base de données du modèle considéré, et voir si l'objet recherché est trouvé ou non. +Ce Q object sera donc utilisé pour savoir si l'instance que l'on veux modifier est concernée par notre permission. Exception faite sur l'ajout d'objets : l'objet n'existant pas encore en base de données, il est ajouté puis supprimé à la volée, en prenant soin de désactiver les signaux. -## Compilation de la requête +## Compilation de la query -La requête est enregistrée sous un format JSON, puis est traduite en requête `Q` récursivement en appliquant certains paramètres (on note `about` la fonction de traduction) : +La query est enregistrée sous un format JSON, puis est traduite en requête `Q` récursivement en appliquant certains paramètres. +Le fonctionnemente de base des permission peux être décris avec les differents opérations : -* `[] | {} -> Q(pk=F("pk"))` Une liste ou un objet vide représente tous les objets -* `["AND", query, …] -> about(query) & … ` Conjonction de requêtes -* `["OR", query, …] -> about(query) | … ` Disjonction de requêtes -* `["NOT", query] -> ~about(query) ` Négation d'une requête -* `{key: value, …} -> Q(key=value, …) ` Liste de champs et de valeurs dans un objet Q - * `key` est une chaîne de caractères,représentant le nom du champ - * `value` peut être de plusieurs natures : - * Un nombre, une chaîne de caractères, un booléen, `null` pour représenter des valeurs littérales - * Si c'est une liste, c'est interprété comme un paramètre - * Le premier argument de la liste représente le nom du paramètre. Ce peut être l'objet utilisateur `user`, l'objet club `club` ou des objets de classe (`Note`, ...). - * S'il y a des arguments suivants dans la liste, ils vont être interprétés comme des sous-attributs du paramètre initial. - * Si un argument est une liste, alors c'est interprété comme une fonction à appeler, dont les arguments sont donnés par les autres éléments de la liste. Si cette liste contient un dictionnaire, le dictionnaire est passé en argument à la fonction sous la forme de kwargs. - * Les exemples seront plus clairs. - * Si c'est un dictionnaire de la forme {"F": oper}, alors c'est interprété comme un objet `F`. - * Si `oper` est un littéral, il est interprété tel quel. - * Si `oper = ["ADD", oper1, oper2, …]`, le résultat est la somme des objets `F` calculés d'après le reste de la liste. - * Si `oper = ["SUB", oper1, oper2,]`, le résultat est la différence des deux objets `F`. - * Si `oper = ["MUL", oper1, oper2, …]`, le résultat est le produit des objets `F` calculés d'après le reste de la liste. - * Si `oper = ["F", name]`, le résultat est F(name). - * Dans les autres cas, `oper` est un paramètre calculé comme précédemment. +| opérations | JSON | Q object | +|----------------|---------------------------|---------------------------------| +| ALL | `[]` ou `{}` | `.objects.get(Q(pk=F("pk"))` | +| ET | `["AND", Q_1, Q_2, ... ]` | `.objects.get(Q_1 & Q_2 & ...)` | +| OU | `["OR", Q_1, Q_2, ...]` | `.objects.get(Q_1 \| Q_2 \| ...)` | +| NON | `["NOT", Q_1]` | `.objects.get(~Q_1)` | +| key == value | `{key: value}` | `.objects.get(Q(key = value))` | +De plus value peux être utilisé de différetentes manières: + * Un nombre, une chaîne de caractères, un booléen, `null` pour représenter des valeurs littérales + * Si c'est une liste, c'est interprété comme un paramètre + * Le premier argument de la liste représente le nom du paramètre. Ce peut être l'objet utilisateur `user`, l'objet club `club` ou des objets de classe (`Note`, ...). + * S'il y a des arguments suivants dans la liste, ils vont être interprétés comme des sous-attributs du paramètre initial. + * Si un argument est une liste, alors c'est interprété comme une fonction à appeler, dont les arguments sont donnés par les autres éléments de la liste. Si cette liste contient un dictionnaire, le dictionnaire est passé en argument à la fonction sous la forme de kwargs. + * Si c'est un dictionnaire de la forme {"F": oper}, alors c'est interprété comme un objet `F`. + * Si `oper` est un littéral, il est interprété tel quel. + * Si `oper = ["ADD", oper1, oper2, …]`, le résultat est la somme des objets `F` calculés d'après le reste de la liste. + * Si `oper = ["SUB", oper1, oper2,]`, le résultat est la différence des deux objets `F`. + * Si `oper = ["MUL", oper1, oper2, …]`, le résultat est le produit des objets `F` calculés d'après le reste de la liste. + * Si `oper = ["F", name]`, le résultat est F(name). + * Dans les autres cas, `oper` est un paramètre calculé comme précédemment. +Vous n'avez rien compris ? Voilà des exemples: ## Exemples -Cette définition n'étant pas claire, voici quelques exemples facilitant la compréhension : +* Permission sur le model `User` avec comme query: + ```js + {"is_superuser": true} + ``` + > si l'utilisateur cible est un super utilisateur. -```js -Modèle : User -Q(is_superuser=True) := {"is_superuser": true} -Permission accordée si l'utilisateur cible est un super utilisateur. -``` +* sur le model `Note`: + ```js + {"pk": + ["user","note", "pk"] + } + ``` + > si l'identifiant de la note cible est l'identifiant de l'utilisateur dont on regarde la permission. -```js -Modèle : Note -Q(pk=user.note.pk) := {"pk": ["user", "note", "pk"]} -Permission accordée si l'identifiant de la note cible est l'identifiant de l'utilisateur dont on regarde la permission. -``` +* sur le model `Transaction`: -```js -Modèle : Transaction -["AND", {"source": ["user", "note"]}, {"amount__lte": ["user", "note", "balance"]}] -Permission accordée si la source est la note de l'utilisateur et si le montant est inférieur à son solde. -``` + ```js + ["AND", + {"source": + ["user", "note"]}, + {"amount__lte": + ["user", "note", "balance"]} + ] + ``` + > si la source est la note de l'utilisateur et si le montant est inférieur à son solde. -```js -Modèle : Alias -["OR", {"note__in": ["NoteUser", "objects", ["filter", {"user__membership__club__name": "Kfet"}], ["all"]]}, {"note__in": ["NoteClub", "objects", ["all"]]}] -Permission accordée si l'alias appartient à une note de club ou s'il appartient à la note d'un utilisateur membre du club Kfet. -``` +* Sur le model `Alias` + + ```js + ["OR", + {"note__in": + ["NoteUser", "objects",[ + "filter",{ + "user__membership__club__name": "Kfet" + }], + ["all"] + ]}, + {"note__in": + ["NoteClub", "objects", ["all"]] + } + ] + ``` + > si l'alias appartient à une note de club ou s'il appartient à la note d'un utilisateur membre du club Kfet. + +* sur le model `Transaction` + + ```js + ["AND", + {"destination": ["club", "note"]}, + {"amount__lte": + {"F": [ + "ADD", + ["F", "source__balance"], + 5000] + } + } + ] + ``` + > si la destination est la note du club dont on est membre et si le montant est inférieur au solde de la source + 50 €, autrement dit le solde final est au-dessus de -50 €. -```js -Modèle : Transaction -["AND", {"destination": ["club", "note"]}, {"amount__lte": {"F": ["ADD", ["F", "source__balance"], 5000]}}] -Permission accordée si la destination est la note du club dont on est membre et si le montant est inférieur au solde de la source + 50 €, autrement dit le solde final est au-dessus de -50 €. -``` ## Masques de permissions