Prévenir un piège de sécurité des règles d'accès : une comparaison entre deux champs vides peut accorder l'accès à tort

Contexte

Suite à mon fil précédent sur le déréférencement à travers une Référence, même document de formation, même schéma de tables :

  • Clients (Adresse_mail, etc.)
  • Commandes (Client → Référence vers Clients, renseignée après coup : une commande peut exister sans client assigné le temps qu’un commercial la traite)

Attribut utilisateur configuré (inchangé par rapport au fil précédent) :

  • Nom : Client
  • Propriété d’appairage : user.Email
  • Table d’appairage : Clients
  • Colonne cible : Adresse_mail

Suivant la solution du fil précédent, la règle de ligne sur Commandes compare une colonne miroir aplatie à l’attribut utilisateur :

$Client_email == user.Client.Adresse_mail

Comportement attendu

Seul le client concerné par une commande (ou un utilisateur enregistré comme tel dans la table Clients) doit voir la ligne correspondante. Un utilisateur connecté qui n’est pas un client enregistré ne doit voir aucune commande.

Comportement observé (le piège)

Sur une commande dont le champ Client n’a pas encore été renseigné ($Client_email vaut donc None), n’importe quel utilisateur connecté qui n’est pas un client enregistré (donc pour qui user.Client.Adresse_mail vaut lui aussi None) voit malgré tout la ligne. La règle matche alors qu’elle ne devrait matcher pour personne.

Cause

None == None vaut True en Python. Les deux côtés de la comparaison peuvent être vides pour des raisons totalement indépendantes :

  • côté rec : la colonne miroir vaut None tant que la commande n’a pas de client assigné (situation normale et temporaire) ;
  • côté user : le lookup de l’attribut utilisateur vaut None dès que l’utilisateur connecté n’a pas de correspondance dans la table Clients (situation normale pour n’importe quel utilisateur qui n’est pas censé avoir ce rôle, un autre commercial par exemple).

Ces deux None sans rapport l’un avec l’autre suffisent à rendre la comparaison vraie.

Solution

Garder systématiquement la comparaison contre le cas où l’un des deux côtés est vide, avant de comparer :

$Client_email and user.Client.Adresse_mail and $Client_email == user.Client.Adresse_mail

Méthode de test qui a permis de le découvrir

Le test « Voir en tant que » sur le cas nominal (le bon client, une commande bien assignée) ne suffit pas à détecter ce piège : il faut spécifiquement tester le cas négatif (un utilisateur qui ne devrait avoir accès à rien, sur une ligne où le champ de rattachement est encore vide) pour le voir apparaître.

Question

Comme pour mon fil précédent : comportement déjà documenté ailleurs (une référence serait bienvenue), ou suffisamment silencieux pour mériter un avertissement dans la documentation officielle des règles d’accès ? La combinaison « champ de Référence pas encore renseigné + comparaison naïve avec un attribut utilisateur » me semble un cas assez générique pour toucher beaucoup de documents Grist en production, au-delà de mon cas d’usage.

Bien vu et analysé !

Dans mon esprit, si j’attribue une règle de lecture basée sur la table des clients, je ne résous pas pour autant la question des autres utilisateurs. Ici, j’ai l’impression que la situation pourrait se résoudre avec :

  • soit une seule table Utilisateurs (si tous les utilisateurs sont dans la table alors il n’y a plus d’effet de bord pour les commerciaux par exemple)
  • soit une deuxième règle « catch all » pour régler le cas des autres utilisateurs.