Un point de départ pour maîtriser les permissions avancées

Bonjour à toustes,

Nos interlocuteur·ice·s à Open Source Politics nous disent souvent être intimidé·e·s par les permissions avancées de Grist, et on les comprend : elles peuvent vite devenir un sac de nœuds si on ne prend pas le temps de bien comprendre leur fonctionnement. C’est ce que j’ai essayé de faire, et je vous propose désormais cette ébauche de méthode qui devrait aider les débutant·e·s à mettre le pied à l’étrier. Je suis particulièrement preneur de vos retours d’expérience en la matière !

À la fin de ce tutoriel, vous saurez comment répartir les utilisateurices de votre document en catégories, et leur donner le droit de modifier des tableaux spécifiques, voire des lignes de tableaux spécifiques.

Vous pouvez tester ce tuto en consultant le document de démonstration : après avoir créé votre copie de travail, vous en serez logiquement l’OWNER.

Tout d’abord, pensez votre modèle de permissions

Par défaut, Grist définit des permissions pour quatre catégories d’utilisateurices :

  • les OWNER qui peuvent tout faire ;
  • les EDITOR qui peuvent faire tout ce que font les OWNER sauf modifier les permissions ou partager le document ;
  • les VIEWER qui ne peuvent que lire ;
  • et tous les autres qui ne peuvent rien lire (et donc rien modifier) :

Dans nos cas d’usage, ces 4 catégories ne suffisent pas : le plus souvent, on va avoir besoin de plusieurs catégories intermédiaires entre les VIEWER et les EDITOR. C’est le moment de réfléchir à ces catégories intermédiaires dont on va avoir besoin, autrement dit, de concevoir un modèle de permissions. Pour ce faire, je m’applique ces 3 règles simples :

  1. « tout fermer par défaut »
  2. lister mes catégories de la plus permissive à la plus restrictive
  3. créer au moins une table d’attribution avec des références bidirectionnelles

Voyons ces 3 règles en détail :

  • « tout fermer par défaut » est un conseil déjà donné par @ecouillard : il s’agit de faire table rase des règles par défaut de Grist pour ensuite « ouvrir progressivement la porte » et accorder petit à petit les permissions nécessaires. On peut faire ça ainsi :
    • en décochant la case « Autorise les éditeurs à éditer la structure » (comme indiqué dans la doc officielle Grist)
    • puis en ajoutant une règle par défaut « Tout refuser » aux EDITOR
  • Lister les catégories vous permettra ensuite d’y voir plus clair dans la façon dont vous allez « ouvrir progressivement la porte ». Personnellement, je raisonne en partant de la catégorie la plus permissive (qui sera forcément OWNER) jusqu’à la plus restrictive. Voilà à quoi cela peut ressembler :
Catégorie Permissions
OWNER Modifier tout
Responsables Modifier tout sauf les permissions
Directions Lire tout & Ne modifier que les lignes taguées « DG »
Services Ne lire et modifer que les lignes taguées avec leur service
VIEWER Lire tout & Ne rien modifier
Élus Ne lire que les lignes taguées avec leur mairie & Ne rien modifier
EDITOR Ne rien lire (donc ne rien modifier)

Ce n’est pas le tableau que j’ai implémenté dans mon document de démonstration, qui est beaucoup plus simple ; mais c’est le genre de complexité que vous pourrez être amené à formuler.

  • Enfin, vous allez pouvoir créer dans votre document un tableau d’attribution, pour reprendre les termes de la documentation Grist, qui va formaliser ce modèle de permission. Pour me simplifier la vie en tant qu’OWNER (qui, je le rappelle, est la seule catégorie à pouvoir modifier les permissions), je vais en réalité créer deux tableaux d’attribution :
    • un qui liste les catégories et rappelle leurs permissions (c’est grosso modo le tableau que vous aurez imaginé à l’étape précédente).
    • un autre qui liste les personnes utilisateurices, avec l’adresse email de leur compte Grist dans une colonne, et leur(s) catégorie(s) dans une autre, qui sera de type Référence multiple. L’astuce ici consiste à ajouter une référence bidirectionnelle sur cette colonne (via le panneau de création) : de cette manière, vous pourrez voir en un coup d’œil quelles personnes font partie de quelle catégorie.

Il restera une colonne à ajouter : une formule de conversion des catégories, qui sont donc de type Référence multiple mais qu’il faudra passer en type Texte pour pouvoir les manipuler dans les permissions avancées. Comme l’expliquait @audezu dans un autre post, il faut donc, dans la table des personnes, créer une colonne formule qu’on pourra appeler « CatégorieTXT », avec la formule $Categorie.Nom (à supposer que la colonne des catégories a bien pour identifiant $Categorie et que, dans la table des catégories, la colonne contenant les noms des catégories a pour identifiant $Nom).

Connecter votre table d’attribution aux permissions avancées

Vos tableaux d’attribution vont pouvoir servir de base aux règles qu’il faut à présent ajouter aux permissions avancées de votre document Grist : « si l’utilisateurice est dans telle catégorie, alors on lui accorde telles permissions ». Mais avant tout, Grist doit être en mesure de relier une catégorie aux personnes qui en font partie. Il faut pour cela créer une propriété d’utilisateur, comme indiqué dans la documentation officielle :

  • dans la page « Permissions avancées », cliquez sur « Ajouter des propriétés d’utilisateur »
  • une ligne apparaît : vous pouvez la remplir comme sur l’image ci-dessous. Mettez ce que vous volez dans « Name », mais prenez soin d’indiquer user.Email en propriété d’appairage, et de ne pas vous tromper de table d’attribution : la « Colonne cible » doit être celle qui contient l’adresse email des comptes Grist des utilisateurices.

N’oubliez pas de cliquer sur « Enregistrer » en haut à gauche !

Permettre l’accès à certaines tables seulement

Nous pouvons désormais définir des règles pour chaque catégorie renseignée dans la table d’attribution. Celle de mon document de démo comporte 4 catégories :

Catégorie Permissions
Équipe Meta Peut tout modifier sauf les permissions
Équipe A Ne peut lire et modifier que la table « Agenda de la parentalité »
Équipe B Ne peut lire et modifier que la table « Propositions de budget participatif »
Équipe C Je n’en ai pas défini, donc par défaut, ne peut rien lire

On sait que l’équipe C (et les futures équipes) ne verront rien tant qu’on n’aura pas « ouvert progressivement la porte », car nous avons « fermé la porte » à l’étape précédente. Donc, « ouvrons » cette porte pour les équipes A et B. Heureusement, c’est très simple :

  • dans la page « Permissions avancées », cliquez sur « Ajouter des règles pour la table ». Attention, on parle bien ici de tables, et non de pages Grist : le contrôle s’effectue sur les tables listées dans la page « Données sources ». À vous ensuite de répartir intelligemment les vues de ces tables dans les pages appropriées (à ce sujet, cf. « Limitations » ci-dessous).
  • Une fois la règle affichée, une seule condition suffit : "Le nom de la catégorie" in user.LeNomDeLaPropriété.CategorieTXT".

Et si on veut ouvrir la porte plus grand ? Il suffit de rajouter une règle générale, avec exactement la même syntaxe :

Encore une fois : n’oubliez pas de cliquer sur « Enregistrer » en haut à gauche !

Permettre l’accès à certaines lignes seulement

Pour faire en sorte que les membres d’une catégorie donnée ne puissent voir que les lignes qui les concernent dans une table donnée, on va également créer une condition sur ladite table, mais elle sera un peu plus compliquée :

  • pour commencer, si on veut filtrer par ligne en même temps que par table, il nous faut créer des sous-catégories auxquelles affecter les lignes en question. Il va donc falloir créer une troisième table d’attribution, là aussi avec sa référence bidirectionnelle.
  • ensuite, il faut ajouter à la table qu’on désire filtrer une colonne de référence simple (cf. « Limitations ») dans laquelle on pourra renseigner la sous-catégorie de chaque ligne.
  • on peut désormais retourner sur la page Permissions avancées, et à nouveau « Ajouter des règles pour la table », avec une syntaxe de ce type : ("Équipe A" in user.Utilisateurice.CategorieTXT and rec.Perimetre in user.Utilisateurice.Perimetre).

Ne quittez pas la page sans cliquer sur « Enregistrer » !

Rajouter des lignes quand on n’a accès qu’à certaines d’entre elles

C’est un cas qui peut se présenter : vous voulez qu’une catégorie d’utilisateurices puisse rajouter de la donnée, mais uniquement dans sa propre catégorie. Voilà comment je m’y suis pris :

  • j’ai dû, comme précédemment, rajouter une colonne de conversion, cette fois-ci dans la table d’attribution des sous-catégories, pour convertir les adresses des utilisateurices en texte : la colonne s’appelle « Personnes_emailTXT » et a pour formule $Personnes.Email
  • puis, dans les Permissions avancées :
    • j’ajoute une règle sur la table « Sous-catégories », avec la condition user.Utilisateurice.Email in rec.Personnes_emailTXT, et un droit de lecture seule. Cette règle n’a pas besoin de concerner la table entière, seulement la colonne qui contient les noms des sous-catégories (on ne veut pas que les utilisateurices voient plus d’informations que nécessaire) : vous pouvez filtrer la règle en cliquant sur le bouton à trois points en haut à droite de l’encadré de la règle et en choisissant « Ajouter une règle de colonne » (vous pourrez alors supprimer la condition vide pour « All »)

    • je modifie la règle de la table où je veux autoriser l’ajout de lignes, en ajoutant un and à la condition existante : and newRec.Perimetre in user.Utilisateurice.Sous_categorie. C’est ce newRec qui permettra à l’utilisateurice de rajouter des lignes, et seules ses catégories devraient lui être proposées dans le menu déroulant de la colonne de référence.

À ce stade, vous allez peut-être remarquer qu’il paraît contradictoire de donner un droit de lecture sur une table d’attribution : ces dernières ne devraient être visibles que par les OWNER, n’est-ce pas ? Pour ne pas que l’utilisateurice voie une table « Sous-catégories » toute vide à l’exception des cellules qui mentionnent ses catégories, il y a une astuce† : mettre la page qui contient la vue de cette table en sous-page d’une autre page qui, elle, contiendra la vue d’une table pour laquelle les utilisateurices n’ont aucun droit de lecture. C’est le cas dans notre modèle de démonstration : la table « Personnes » n’a aucune règle spécifique dans les Permissions avancées, par conséquent, elle est masquée aux EDITOR par défaut. Ainsi, tout ce qui sera dans les sous-pages de cette table sera pareillement masqué. Si ça vous embête de créer une « fausse » table uniquement pour ça, vous pouvez obtenir le même résultat en créant une table d’agrégation (via le bouton « Σ » vert), dont les droits ne seront pas hérités de la table sur laquelle elle est basée.

† je ne me rappelle pas avoir aperçu cette astuce dans la documentation officielle (si vous la voyez, dites-le moi !), je ne peux donc pas garantir qu’elle fonctionnera encore sur les prochaines versions de Grist.

Limitations

  • Il faut nécessairement créer une colonne « CatégorieTXT » pour convertir une référence multiple en texte et pouvoir ajouter une condition in sur cette colonne. Je n’ai pas trouvé de meilleur workaround malheureusement.
  • Sur certaines instances de Grist, les pages dans lesquelles on a mis des vues sur des tables dont les règles de permissions avancées sont différentes s’affichent mal. C’est pour ça que dans mon document de démonstration, chaque table a sa propre page, ce qui n’est pas la disposition la plus élégante. Je vous recommande cependant de faire de même en attendant de pouvoir investiguer cette anomalie.
  • J’ai essayé d’ajouter des références multiples plutôt que des références simples à la table « Agenda de la parentalité », mais cela nous fait nous heurter au problème de l’intersection des listes. J’ai essayé d’utiliser la fonction issubset(), mais le Python n’est pas (encore) mon fort. À défaut, il faudra se contenter de typer la colonne en Référence simple.
  • Certains widgets risquent malheureusement de tomber en panne en appliquant ce modèle. C’est un problème connu, qu’Aude explique en détail sur cet autre topic (merci à elle !)
7 « J'aime »

Top ce tuto, merci !
Par contre, je n’ai pas accès aux ACL du document test :

Dans quel ordre sont lus les lignes des ACL qui s’appliquent à toutes les tables ? Car j’ai les lignes grises qui sont contradictoires avec ce que j’écris au-dessus. Par exemple user.Access pour les EDITOR.

Bonjour Mathieu, désolé c’est une bévue de ma part : j’ai modifié le lien pour pointer vers le bon document. Tu devrais pouvoir créer ta propre copie à partir de celui-là !

je remarque que tu as le même problème que moi dans ton fichier démo. Connecte toi en Bill Gates et tu verras la table Sous-catégorie vide. Pourtant par défaut, il ne devrait pas la voir. Je n’arrive pas à la cacher sans complexifier outre mesure les ACL de cette table


Eh oui, j’aurais dû le préciser dans « Limitations », mais tu l’as bien remarqué : comme il m’a fallu rajouter la règle de lecture seule sur une colonne de « Sous-catégories » (nécessaire pour autoriser l’ajout de lignes au tableau « agenda de la parentalité »), toutes les personnes se retrouvent capables de voir cette colonne même si leur email n’y figure pas (elles la voient donc vide, mais elles la voient quand même). J’aimerais bien trouver une approche plus élégante pour éviter ça.

On a pu faire remonter l’info à Grist Labs ou Grist Gouv ? Est-ce un bug ou une feature ?
@nicolas.imbert @audezu vous sauriez nous aiguiller ?

Le problème ne vient pas de ce que tu ajoutes pour Agenda de la parentalité mais juste de la ligne ici :

J’ai testé ça :

Mais ça ne permet pas de cacher la table et j’ai ce message d’erreur :

Bonjour,
Je voudrais permettre à un utilisateur de modifier uniquement une colonne d’une seule table.
Je l’ai mis VIEWER sur le document et lui donne le droit UPDATE par son email sur le champ de la table.
Quand je fais « voir en tant que », le droit n’est pas accordé.

Suis-je obligée de placer la personne comme EDITOR du document ?
Ce qui veut dire qu’ensuite il faudrait que je restreigne ses accès à toutes les tables ?

Merci d’avance

Bonjour Patricia,
Nous avons déjà restreints les droits des EDITOR dans notre modèle en leur « fermant la porte » dans l’encadré des Règles par défaut (cf. capture d’écran plus haut). Étant donné qu’on procède ici en « ouvrant progressivement » les permissions, il faut donc d’abord ajouter les utilisateurices en EDITOR, puis leur accorder des droits soit via les tables d’attribution, soit au cas-par-cas directement via la page des Permissions avancées. Les VIEWER n’ont pas vocation à voir leurs droits élargis : par défaut, ils ne peuvent que voir sans modifier.

1 « J'aime »

Merci Laurent,
Mais je ne m’en sors pas.

J’ai bien tout restreint pour les EDITOR

Sur la table entière j’ai bloqué l’UPDATE des EDITOR mais accordé le READ
Sur le champ j’accorde l’UPDATE et READ à EDITOR (et aussi via une table d’attribution par l’email)

Quand je teste, mon utilisateur EDITOR ne peut pas modifier le champ
Et quand j’élargis un peu les droits il peut tout modifier…

Je n’arrive pas à comprendre où ça coince :sos:

J’ai essayé de reproduire votre modèle de permissions dans le document de démonstration : quand je fais « Voir en tant que » via un compte EDITOR, j’arrive bien à modifier la colonne « MEAI » et non les autres. La seule différence que je vois entre votre modèle et le mien est la règle par défaut user.Access == EDITOR que j’ai rédigée user.Access in EDITOR (ce qui ne devrait pas entraîner de différence me semble t-il).

Avez-vous essayé d’importer ce document dans une autre instance de Grist, par exemple celle de https://www.getgrist.com/ ?

Merci Laurent, après moultes essais, et même sur l’instance grist que vous m’avez conseillée j’ai fini par essayer sur une nouvelle table basic et ça fonctionnait. Et j’ai fini par repérer que j’avais 2 champs qui s’appelaient MEAI dans ma table…Et donc tout va très bien :slight_smile:. désolée de vous avoir pris du temps.
Par contre depuis, je remarque que je ne peux plus consulter l’historique du document " History blocked because of access rules". C’est contournable ?

J’ai trouvé réponse à ma question, c’est parce que je m’étais (OWNER) bloqué des tables en LECTURE