blog.notmyidea.org/feeds/all-fr.atom.xml
2019-11-07 17:27:36 +01:00

455 lines
No EOL
46 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis - Carnets en ligne</title><link href="https://blog.notmyidea.org/" rel="alternate"></link><link href="https://blog.notmyidea.org/feeds/all-fr.atom.xml" rel="self"></link><id>https://blog.notmyidea.org/</id><updated>2015-07-07T00:00:00+02:00</updated><entry><title>Service de nuages : Perspectives pour l'été</title><link href="https://blog.notmyidea.org/service-de-nuages-perspectives-pour-lete-fr.html" rel="alternate"></link><published>2015-07-07T00:00:00+02:00</published><updated>2015-07-07T00:00:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2015-07-07:/service-de-nuages-perspectives-pour-lete-fr.html</id><summary type="html">&lt;p class="first last"&gt;Le travail en cours et les fonctionnalités à venir pour les prochains mois.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Mozilla a pour coutume d'organiser régulièrement des semaines de travail où tous les employés
sont réunis physiquement. Pour cette dernière édition, nous avons pu retrouver
nos collègues du monde entier à &lt;a class="reference external" href="http://www.openstreetmap.org/node/268148288#map=4/50.12/-122.95"&gt;Whistler, en Colombie Britannique au Canada&lt;/a&gt; !&lt;/p&gt;
&lt;img alt="«All Hands» talk about Lego, by &amp;#64;davidcrob - CC0" class="align-center" src="{filename}/images/whistler-talks.jpg" /&gt;
&lt;p&gt;Ce fût l'occasion pour notre équipe de se retrouver, et surtout de partager notre
vision et nos idées dans le domaine du stockage, afin de collecter des cas d'utilisation pour
notre solution &lt;a class="reference external" href="https://kinto.readthedocs.org"&gt;Kinto&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dans cet article, nous passons en revue les pistes que nous avons pour
les prochains mois.&lt;/p&gt;
&lt;div class="section" id="ateliers-et-promotion"&gt;
&lt;h2&gt;Ateliers et promotion&lt;/h2&gt;
&lt;p&gt;Nicolas a présenté &lt;a class="reference external" href="https://github.com/mozilla-services/kinto.js"&gt;Kinto.js&lt;/a&gt; dans un atelier dédié, avec comme support de
présentation le &lt;a class="reference external" href="http://kintojs.readthedocs.org/en/latest/tutorial/"&gt;tutorial d'introduction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;L'application résultante, pourtant toute simple, permet d'appréhender les
concepts de synchronisation de Kinto. Le tout sans installation prélable,
puisque Rémy a mis en place un &lt;a class="reference external" href="https://kinto.dev.mozaws.net/v1/"&gt;serveur de dev effacé tous les jours&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nous avions mis un point d'honneur à faire du Vanilla.JS, déjà pour éviter les
combats de clochers autour des frameworks, mais aussi pour mettre en évidence qu'avec
HTML5 et ES6, on n'était plus aussi démunis qu'il y a quelques années.&lt;/p&gt;
&lt;p&gt;Ce petit atelier nous a permis de nous rendre compte qu'on avait encore de
grosses lacunes en terme de documentation, surtout en ce qui concerne
l'éco-système et la vision globale des projets (Kinto, Kinto.js, Cliquet, ...).
Nous allons donc faire de notre mieux pour combler ce manque.&lt;/p&gt;
&lt;img alt="Kinto.js workshop - CC0" class="align-center" src="{filename}/images/whistler-workshop.jpg" /&gt;
&lt;/div&gt;
&lt;div class="section" id="mozilla-payments"&gt;
&lt;h2&gt;Mozilla Payments&lt;/h2&gt;
&lt;p&gt;Comme &lt;a class="reference external" href="http://www.servicedenuages.fr/la-gestion-des-permissions"&gt;décrit précédemment&lt;/a&gt;, nous avons mis en place un système de permissions pour répondre aux besoins de suivi des paiements et abonnements.&lt;/p&gt;
&lt;p&gt;Pour ce projet, Kinto sera utilisé depuis une application Django, via un client Python.&lt;/p&gt;
&lt;p&gt;Maintenant que les développements ont été livrés, il faut transformer l'essai, réussir l'intégration, l'hébergement et la montée en puissance. La solution doit être livrée à la fin de l'année.&lt;/p&gt;
&lt;div class="section" id="a-venir"&gt;
&lt;h3&gt;À venir&lt;/h3&gt;
&lt;p&gt;Nous aimerions en profiter pour implémenter une fonctionnalité qui nous tient à coeur : la construction de la liste des enregistrements accessibles en lecture sur une collection partagée.&lt;/p&gt;
&lt;img alt="Whistler Alta Lake - CC0" class="align-center" src="{filename}/images/whistler-lake.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="firefox-os-et-stockage"&gt;
&lt;h2&gt;Firefox OS et stockage&lt;/h2&gt;
&lt;p&gt;Nous avons eu beaucoup d'échanges avec l'équipe de Firefox OS, avec qui nous avions
déjà eu l'occasion de collaborer, pour le &lt;a class="reference external" href="https://github.com/mozilla-services/msisdn-gateway"&gt;serveur d'identification BrowserID par SMS&lt;/a&gt; et pour &lt;a class="reference external" href="https://github.com/mozilla-services/loop-server"&gt;Firefox Hello&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="in-app-sync"&gt;
&lt;h3&gt;In-App sync&lt;/h3&gt;
&lt;p&gt;Kinto, la solution simple promue pour la synchronisation de données dans les applications
Firefox OS ? La classe ! C'est ce qu'on avait en tête depuis longtemps, déjà à
l'époque avec &lt;a class="reference external" href="http://daybed.readthedocs.org/"&gt;Daybed&lt;/a&gt;. Voici donc une belle opportunité à saisir !&lt;/p&gt;
&lt;p&gt;Il va falloir expliciter les limitations et hypothèses simplificatrices de notre
solution, surtout en termes de gestion de la concurrence. Nous sommes persuadés
que ça colle avec la plupart des besoins, mais il ne faudrait pas décevoir :)&lt;/p&gt;
&lt;p&gt;Le fait que &lt;a class="reference external" href="https://github.com/daleharvey"&gt;Dale&lt;/a&gt;, un des auteurs de &lt;a class="reference external" href="http://pouchdb.com/"&gt;PouchDB&lt;/a&gt; et &lt;a class="reference external" href="https://github.com/michielbdejong"&gt;Michiel de Jong&lt;/a&gt;, un des auteurs de &lt;a class="reference external" href="http://remotestorage.io/"&gt;Remote Storage&lt;/a&gt;, nous aient encouragés sur nos premiers pas nous a bien motivé !&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cut-the-rope"&gt;
&lt;h3&gt;Cut the Rope&lt;/h3&gt;
&lt;p&gt;Kinto devrait être mis à profit pour synchroniser les paramètres et les scores
du &lt;a class="reference external" href="http://mozilla.cuttherope.net/"&gt;jeu&lt;/a&gt;. Un premier exercice et une première vitrine sympas !&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="syncto"&gt;
&lt;h3&gt;« SyncTo »&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://docs.services.mozilla.com/storage/apis-1.5.html"&gt;Firefox Sync&lt;/a&gt; est la solution qui permet de synchroniser les données de Firefox (favoris, extensions, historique, complétion des formulaires, mots de passe, ...) entre plusieurs périphériques, de manière chiffrée.&lt;/p&gt;
&lt;p&gt;L'implémentation du client en JavaScript est relativement complexe et date un peu maintenant.
Le code existant n'est pas vraiment portable dans &lt;em&gt;Firefox OS&lt;/em&gt; et les tentatives de réécriture
n'ont pas abouti.&lt;/p&gt;
&lt;p&gt;Nous souhaitons implémenter un pont entre &lt;em&gt;Kinto&lt;/em&gt; et &lt;em&gt;Firefox Sync&lt;/em&gt;, de manière
à pouvoir utiliser le client &lt;em&gt;Kinto.js&lt;/em&gt;, plus simple et plus moderne, pour récupérer
les contenus et les stocker dans IndexedDB. Le delta à implémenter côté serveur est faible car nous nous étions
inspirés du protocole déjà éprouvé de Sync. Côté client, il s'agira surtout de
câbler l'authentification BrowserId et la Crypto.&lt;/p&gt;
&lt;p&gt;Alexis a sauté sur l'occasion pour commencer l'écriture d'&lt;a class="reference external" href="https://github.com/mozilla-services/syncclient"&gt;un client python pour Firefox Sync&lt;/a&gt;, qui servira de brique de base pour l'écriture du service.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cloud-storage"&gt;
&lt;h3&gt;Cloud Storage&lt;/h3&gt;
&lt;p&gt;Eden Chuang et Sean Lee ont présenté les avancées sur l'intégration de services de stockages
distants (&lt;em&gt;DropBox, Baidu Yun&lt;/em&gt;) dans &lt;em&gt;Firefox OS&lt;/em&gt;. Actuellement, leur preuve de
concept repose sur &lt;a class="reference external" href="https://fr.wikipedia.org/wiki/Filesystem_in_Userspace"&gt;FUSE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nous avons évidemment en tête d'introduire la notion de fichiers attachés dans
&lt;em&gt;Kinto&lt;/em&gt;, en implémentant la specification
&lt;a class="reference external" href="https://tools.ietf.org/html/draft-dejong-remotestorage-05"&gt;*Remote Storage*&lt;/a&gt;,
mais pour l'instant les cas d'utilisations ne se sont pas encore présentés officiellement.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h3&gt;À venir&lt;/h3&gt;
&lt;p&gt;Nous serons probablement amenés à introduire la gestion de la concurrence dans
le client JS, en complément de ce qui a été fait sur le serveur, pour permettre
les écritures simultanées et synchronisation en tâche de fond.&lt;/p&gt;
&lt;p&gt;Nous sommes par ailleurs perpétuellement preneurs de vos retours — et bien
entendu de vos contributions — tant sur le code &lt;a class="reference external" href="https://github.com/mozilla-services/kinto/"&gt;serveur&lt;/a&gt;
que &lt;a class="reference external" href="https://github.com/mozilla-services/kinto.js/"&gt;client&lt;/a&gt; !&lt;/p&gt;
&lt;img alt="Firefox OS Cloud Storage Presentation - CC0" class="align-center" src="{filename}/images/whistler-cloud-storage.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="contenus-applicatifs-de-firefox"&gt;
&lt;h2&gt;Contenus applicatifs de Firefox&lt;/h2&gt;
&lt;p&gt;Aujourd'hui Firefox a un cycle de release de six semaines. Un des objectifs
consiste à désolidariser certains contenus applicatifs de ces cycles
relativement longs (ex. &lt;em&gt;règles de securité, dictionnaires, traductions, ...&lt;/em&gt;) &lt;a class="footnote-reference" href="#id4" id="id3"&gt;[1]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il s'agit de données JSON et binaire qui doivent être versionnées et synchronisées par
les navigateurs (&lt;em&gt;lecture seule&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Il y a plusieurs outils officiels qui existent pour gérer ça (&lt;em&gt;Balrog&lt;/em&gt;, &lt;em&gt;Shavar&lt;/em&gt;, ...),
et pour l'instant, aucun choix n'a été fait. Mais lors des conversations avec
l'équipe en charge du projet, ce fût vraiment motivant de voir que même pour
ce genre de besoins internes, &lt;em&gt;Kinto&lt;/em&gt; est tout aussi pertinent !&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="id4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id3"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;La bonne nouvelle c'est que toutes les fonctionnalités &lt;em&gt;third-party&lt;/em&gt; qui ont
été intégrées récemment vont redevenir des &lt;em&gt;add-ons&lt;/em&gt; \o/.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;img alt="Landscape - CC0" class="align-center" src="{filename}/images/whistler-landscape.jpg" /&gt;
&lt;/div&gt;
&lt;div class="section" id="awesome-bar"&gt;
&lt;h2&gt;Awesome bar&lt;/h2&gt;
&lt;p&gt;L'équipe &lt;em&gt;Firefox Labs&lt;/em&gt;, le laboratoire qui élève des pandas roux en éprouvette,
serait vraiment intéressé par notre solution, notamment pour abreuver en données
un prototype pour améliorer &lt;em&gt;Awesome bar&lt;/em&gt;, qui fusionnerait URL, historique et recherche.&lt;/p&gt;
&lt;p&gt;Nous ne pouvons pas en dire beaucoup plus pour l'instant, mais les fonctionnalités
de collections d'enregistrements partagées entre utilisateurs de &lt;em&gt;Kinto&lt;/em&gt;
correspondent parfaitement à ce qui est envisagé pour le futur du navigateur :)&lt;/p&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;À venir&lt;/h3&gt;
&lt;p&gt;Nous serons donc probablement amenés, avant de la fin de l'année, à introduire des
fonctionnalités d'indexation et de recherche &lt;em&gt;full-text&lt;/em&gt; (comprendre &lt;em&gt;ElasticSearch&lt;/em&gt;).
Cela rejoint nos plans précédents, puisque c'est quelque chose que nous avions dans
&lt;em&gt;Daybed&lt;/em&gt;, et qui figurait sur notre feuille de route !&lt;/p&gt;
&lt;img alt="Firefox Labs Meeting - CC0" class="align-center" src="{filename}/images/whistler-labs.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="browser-html"&gt;
&lt;h2&gt;Browser.html&lt;/h2&gt;
&lt;p&gt;L'équipe &lt;em&gt;Recherche&lt;/em&gt; explore les notions de plateforme, et travaille notamment
sur l'implémentation d'un navigateur en JS/HTML avec &lt;em&gt;React&lt;/em&gt;:
&lt;a class="reference external" href="https://github.com/mozilla/browser.html"&gt;browser.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kinto&lt;/em&gt; correspond parfaitement aux attentes
de l'équipe pour synchroniser les données associées à un utilisateur.&lt;/p&gt;
&lt;p&gt;Il pourrait s'agir de données de navigation (comme Sync), mais aussi de collections
d'enregistrements diverses, comme par exemple les préférences du navigateur
ou un équivalent à &lt;em&gt;Alexa.com Top 500&lt;/em&gt; pour fournir la complétion d'URL sans
interroger le moteur de recherche.&lt;/p&gt;
&lt;p&gt;L'exercice pourrait être poussé jusqu'à la synchronisation d'états &lt;em&gt;React&lt;/em&gt;
entre périphériques (par exemple pour les onglets).&lt;/p&gt;
&lt;div class="section" id="id7"&gt;
&lt;h3&gt;À venir&lt;/h3&gt;
&lt;p&gt;Si &lt;em&gt;browser.html&lt;/em&gt; doit stocker des données de navigation, il faudra ajouter
des fonctionnalités de chiffrement sur le client JS. Ça tombe bien, c'est un
sujet passionant, et &lt;a class="reference external" href="http://www.w3.org/TR/WebCryptoAPI/"&gt;il y a plusieurs standards&lt;/a&gt; !&lt;/p&gt;
&lt;p&gt;Pour éviter d'interroger le serveur à intervalle régulier afin de synchroniser les
changements, l'introduction des &lt;a class="reference external" href="https://w3c.github.io/push-api/"&gt;*push notifications*&lt;/a&gt; semble assez naturelle.
Il s'agirait alors de la dernière pierre qui manque à l'édifice pour obtenir
un «&lt;em&gt;Mobile/Web backend as a service&lt;/em&gt;» complet.&lt;/p&gt;
&lt;img alt="Roadmap - CC0" class="align-center" src="{filename}/images/whistler-roadmap.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Nous sommes dans une situation idéale, puisque ce que nous avions imaginé
sur &lt;a class="reference external" href="https://github.com/mozilla-services/kinto/wiki/Roadmap"&gt;notre feuille de route&lt;/a&gt; correspond à ce qui nous est demandé par les
différentes équipes.&lt;/p&gt;
&lt;p&gt;L'enjeu consiste maintenant à se coordonner avec tout le monde, ne pas décevoir,
tenir la charge, continuer à améliorer et à faire la promotion du produit, se concentrer
sur les prochaines étapes et embarquer quelques contributeurs à nos cotés pour
construire une solution libre, générique, simple et auto-hébergeable pour le stockage
de données sur le Web :)&lt;/p&gt;
&lt;img alt="Friday Night Party - CC0" class="align-center" src="{filename}/images/whistler-top-roof.jpg" /&gt;
&lt;/div&gt;
</content></entry><entry><title>Service de nuages : Stocker et interroger les permissions avec Kinto</title><link href="https://blog.notmyidea.org/service-de-nuages-stocker-et-interroger-les-permissions-avec-kinto-fr.html" rel="alternate"></link><published>2015-05-26T00:00:00+02:00</published><updated>2015-05-26T00:00:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2015-05-26:/service-de-nuages-stocker-et-interroger-les-permissions-avec-kinto-fr.html</id><summary type="html">&lt;p class="first last"&gt;Comment faire pour stocker et interroger la base de données au sujet des permissions avec Kinto ?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tl;dr: On a maintenant un super système de permission mais comment faire pour stocker et interroger ces permissions de manière efficace ?&lt;/strong&gt;&lt;/p&gt;
&lt;div class="section" id="la-problematique"&gt;
&lt;h2&gt;La problématique&lt;/h2&gt;
&lt;p&gt;Maintenant que nous avons défini un modèle de gestion des permissions
sur les objets qui nous satisfait, le problème est de stocker ces
permissions de manière efficace afin de pouvoir autoriser ou interdire
l'accès à un objet pour la personne qui fait la requête.&lt;/p&gt;
&lt;p&gt;Chaque requête sur notre API va générer une ou plusieurs demandes
d'accès, il faut donc que la réponse soit très rapide sous peine
d'impacter la vélocité du service.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="obtenir-la-liste-des-principals-d-un-utilisateur"&gt;
&lt;h2&gt;Obtenir la liste des &amp;quot;principals&amp;quot; d'un utilisateur&lt;/h2&gt;
&lt;p&gt;Les &lt;em&gt;principals&lt;/em&gt; de l'utilisateur correspondent à son &lt;tt class="docutils literal"&gt;user_id&lt;/tt&gt;
ainsi qu'à la liste des identifiants des groupes dans lesquels il a
été ajouté.&lt;/p&gt;
&lt;p&gt;Pour éviter de recalculer les &lt;em&gt;principals&lt;/em&gt; de l'utilisateur à chaque
requête, le mieux reste de maintenir une liste des &lt;em&gt;principals&lt;/em&gt; par
utilisateur.&lt;/p&gt;
&lt;p&gt;Ainsi lorsqu'on ajoute un utilisateur à un groupe, il faut bien penser
à ajouter le groupe à la liste des &lt;em&gt;principals&lt;/em&gt; de l'utilisateur.&lt;/p&gt;
&lt;p&gt;Ça se complexifie lorsqu'on ajoute un groupe à un groupe.&lt;/p&gt;
&lt;p&gt;Dans un premier temps interdire l'ajout d'un groupe à un groupe est
une limitation qu'on est prêts à accepter pour simplifier le
modèle.&lt;/p&gt;
&lt;p&gt;L'avantage de maintenir la liste des &lt;em&gt;principals&lt;/em&gt; d'un utilisateur
lors de la modification de cette liste c'est qu'elle est déjà
construite lors des lectures, qui sont dans notre cas plus fréquentes
que les écritures.&lt;/p&gt;
&lt;p&gt;Cela nécessite de donner un identifiant unique aux groupes pour tous
les &lt;em&gt;buckets&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Nous proposons de de les nommer avec leur URI:
&lt;tt class="docutils literal"&gt;/buckets/blog/groups/moderators&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="obtenir-la-liste-des-principals-d-un-ace"&gt;
&lt;h2&gt;Obtenir la liste des &amp;quot;principals&amp;quot; d'un ACE&lt;/h2&gt;
&lt;blockquote&gt;
Rappel, un &amp;quot;ACE&amp;quot; est un &lt;em&gt;Access Control Entry&lt;/em&gt;, un des éléments
d'une ACL (e.g. &lt;em&gt;modifier un enregistrement&lt;/em&gt;).&lt;/blockquote&gt;
&lt;p&gt;Avec le &lt;a class="reference external" href="{filename}/2015.05.cliquet-permissions.rst"&gt;système de permissions choisi&lt;/a&gt;, les permissions d'un
objet héritent de celle de l'objet parent.&lt;/p&gt;
&lt;p&gt;Par exemple, avoir le droit d'écriture sur un &lt;em&gt;bucket&lt;/em&gt; permet la
création des permissions et la modification de tous ses records.&lt;/p&gt;
&lt;p&gt;Ce qui veut dire que pour obtenir la liste complète des &lt;em&gt;principals&lt;/em&gt;
ayant une permission sur un objet, il faut regarder à plusieurs
endroits.&lt;/p&gt;
&lt;p&gt;Rémy a &lt;a class="reference external" href="https://gist.github.com/Natim/77c8f61c1d42e476cef8#file-permission-py-L9-L52"&gt;décrit dans un gist la liste d'héritage de chaque permission&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Prenons l'exemple de l'ajout d'un record dans une collection.&lt;/p&gt;
&lt;p&gt;Le droit &lt;tt class="docutils literal"&gt;records:create&lt;/tt&gt; est obtenu si l'on a l'un des droits suivants:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;bucket:write&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;collection:write&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;records:create&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notre première idée était de stocker les permissions sur chaque objet
et de maintenir la liste exhaustive des permissions lors d'une
modification d'ACL. Cependant cela nécessitait de construire cette
liste lors de l'ajout d'un objet et de mettre à jour tout l'arbre lors
de sa suppression. (&lt;em&gt;Je vous laisse imaginer le nombre d'opérations
nécessaires pour ajouter un administrateur sur un *bucket&lt;/em&gt; contenant
1000 collections avec 100000 records chacune.*)&lt;/p&gt;
&lt;p&gt;La solution que nous avons désormais adoptée consiste à stocker les
&lt;em&gt;principals&lt;/em&gt; de chaque &lt;em&gt;ACE&lt;/em&gt; (&lt;em&gt;qui&lt;/em&gt; a le droit de faire telle action
sur l'objet), et de faire l'union des &lt;em&gt;ACE&lt;/em&gt; hérités, afin de les
croiser avec les &lt;em&gt;principals&lt;/em&gt; de l'utilisateur :&lt;/p&gt;
&lt;blockquote&gt;
(ACE(object, permission) inherited_ACE) ∩ PRINCIPALS(user)&lt;/blockquote&gt;
&lt;p&gt;Par exemple l'ACE: &lt;tt class="docutils literal"&gt;/buckets/blog/collections/article:records:create&lt;/tt&gt; hérite de
l'ACE &lt;tt class="docutils literal"&gt;/buckets/blog/collections/article:write&lt;/tt&gt; et de &lt;tt class="docutils literal"&gt;/buckets/blog:write&lt;/tt&gt; :&lt;/p&gt;
&lt;blockquote&gt;
(ACE(/buckets/blog/collections/article:records:create) ACE(/buckets/blog/collections/article:write) ACE(/buckets/blog:write)) ∩ PRINCIPALS('fxa:alexis')&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="recuperer-les-donnees-de-l-utilisateur"&gt;
&lt;h2&gt;Récupérer les données de l'utilisateur&lt;/h2&gt;
&lt;p&gt;La situation se corse lorsqu'on souhaite limiter la liste des
&lt;em&gt;records&lt;/em&gt; d'une collection à ceux accessibles pour l'utilisateur, car
on doit faire cette intersection pour tous les &lt;em&gt;records&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Une première solution est de regarder si l'utilisateur est mentionné
dans les &lt;em&gt;ACL*s du *bucket&lt;/em&gt; ou de la &lt;em&gt;collection&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;Ensuite, si ce n'est pas le cas, alors on filtre les &lt;em&gt;records&lt;/em&gt; pour
lesquels les &lt;em&gt;principals&lt;/em&gt; correspondent à ceux de l'utilisateur.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;principals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_user_principals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;can_read_all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;has_read_perms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;can_read_all&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_all_records&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_read_records&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Il faudra faire quelque chose de similaire pour la suppression
multiple, lorsqu'un utilisateur souhaitera supprimer des
enregistrements sur lesquels il a les droits de lecture mais pas
d'écriture.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-modele-de-donnees"&gt;
&lt;h2&gt;Le modèle de données&lt;/h2&gt;
&lt;p&gt;Pour avoir une idée des requêtes dans un backend SQL, voyons un peu ce
que donnerait le modèle de données.&lt;/p&gt;
&lt;div class="section" id="le-format-des-id"&gt;
&lt;h3&gt;Le format des ID&lt;/h3&gt;
&lt;p&gt;Utiliser des URI comme identifiant des objets présente de nombreux
avantages (lisibilité, unicité, cohérence avec les URLs)&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;bucket: &lt;tt class="docutils literal"&gt;/buckets/blog&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;groupe: &lt;tt class="docutils literal"&gt;/buckets/blog/group/moderators&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;collection: &lt;tt class="docutils literal"&gt;/buckets/blog/collections/articles&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;record: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="les-tables"&gt;
&lt;h3&gt;Les tables&lt;/h3&gt;
&lt;p&gt;Pour le stockage des principals et des permissions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;principals&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;perms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ace&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;principals&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;La table &lt;em&gt;perms&lt;/em&gt; va associer des &lt;em&gt;principals&lt;/em&gt; à chaque &lt;em&gt;ACE&lt;/em&gt;
(e.g.``/buckets/blog:write``).&lt;/p&gt;
&lt;p&gt;Pour le stockage des données:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;write_principals&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;read_principals&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;La colonne &lt;em&gt;parent_id&lt;/em&gt; permet de savoir à qui appartient l'objet
(e.g. groupe d'un &lt;em&gt;bucket&lt;/em&gt;, collection d'un &lt;em&gt;bucket&lt;/em&gt;, &lt;em&gt;record&lt;/em&gt; d'une
collection, ...).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="exemple-d-utilisateur"&gt;
&lt;h3&gt;Exemple d'utilisateur&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fxa:alexis&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fxa:natim&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;/buckets/blog/groups/moderators&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="exemple-d-objets"&gt;
&lt;h3&gt;Exemple d'objets&lt;/h3&gt;
&lt;div class="section" id="bucket"&gt;
&lt;h4&gt;Bucket&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;read_principals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;bucket&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;name&amp;quot;: &amp;quot;blog&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;quot;fxa:alexis&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="group"&gt;
&lt;h4&gt;Group&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;read_principals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog/groups/moderators&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;name&amp;quot;: &amp;quot;moderators&amp;quot;, &amp;quot;members&amp;quot;: [&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;fxa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;natim&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;]}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ce groupe peut être gére par &lt;tt class="docutils literal"&gt;fxa:alexis&lt;/tt&gt; puisqu'il a la permission
&lt;tt class="docutils literal"&gt;write&lt;/tt&gt; dans le &lt;em&gt;bucket&lt;/em&gt; parent.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="collection"&gt;
&lt;h4&gt;Collection&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;read_principals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog/collections/articles&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;collection&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;name&amp;quot;: &amp;quot;article&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;system.Everyone&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;/buckets/blog/groups/moderators&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cette collection d'articles peut être lue par tout le monde,
et gérée par les membres du groupe &lt;tt class="docutils literal"&gt;moderators&lt;/tt&gt;, ainsi que
&lt;tt class="docutils literal"&gt;fxa:alexis&lt;/tt&gt;, via le &lt;em&gt;bucket&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="records"&gt;
&lt;h4&gt;Records&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;read_principals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;record&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog/collections/articles&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;name&amp;quot;: &amp;quot;02f3f76f-7059-4ae4-888f-2ac9824e9200&amp;quot;,&lt;/span&gt;
&lt;span class="s1"&gt; &amp;quot;title&amp;quot;: &amp;quot;Stocker les permissions&amp;quot;, ...}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="interroger-les-permissions"&gt;
&lt;h3&gt;Interroger les permissions&lt;/h3&gt;
&lt;div class="section" id="id1"&gt;
&lt;h4&gt;Obtenir la liste des &amp;quot;principals&amp;quot; d'un ACE&lt;/h4&gt;
&lt;p&gt;Comme vu plus haut, pour vérifier une permission, on fait l'union des
&lt;em&gt;principals&lt;/em&gt; requis par les objets hérités, et on teste leur
intersection avec ceux de l'utilisateur:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;required_principals&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;perms&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ace&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog:write&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog:read&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog/collections/article:write&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/buckets/blog/collections/article:read&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;user_principals&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fxa:natim&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;user_principals&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;required_principals&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="filtrer-les-objets-en-fonction-des-permissions"&gt;
&lt;h4&gt;Filtrer les objets en fonction des permissions&lt;/h4&gt;
&lt;p&gt;Pour filtrer les objets, on fait une simple intersection de liste
(&lt;em&gt;merci PostgreSQL&lt;/em&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;record&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/buckets/blog/collections/article&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_principals&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;principals&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt;
&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_principals&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fxa:natim&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Les listes s'indexent bien, notamment grâce aux &lt;a class="reference external" href="http://www.postgresql.org/docs/current/static/indexes-types.html"&gt;index GIN&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="avec-redis"&gt;
&lt;h3&gt;Avec Redis&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Redis&lt;/em&gt; présente plusieurs avantages pour ce genre de
problématiques. Notamment, il gère les &lt;em&gt;set&lt;/em&gt; nativement (listes de
valeurs uniques), ainsi que les opérations d'intersection et d'union.&lt;/p&gt;
&lt;p&gt;Avec &lt;em&gt;Redis&lt;/em&gt; on peut écrire l'obtention des &lt;em&gt;principals&lt;/em&gt; pour un &lt;em&gt;ACE&lt;/em&gt;
comme cela :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;SUNIONSTORE temp_perm:/buckets/blog/collections/articles:write permission:/buckets/blog:write permission:/buckets/blog/collections/articles:write
SINTER temp_perm:/buckets/blog/collections/articles:write principals:fxa:alexis
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;SUNIONSTORE&lt;/tt&gt; permet de créer un set contenant les éléments de
l'union de tous les set suivants. Dans notre cas on le nomme
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;temp_perm:/buckets/blog/collections/articles:write&lt;/span&gt;&lt;/tt&gt; et il contient
l'union des sets d'ACLs suivants:
- &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;permission:/buckets/blog:write&lt;/span&gt;&lt;/tt&gt;
- &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;permission:/buckets/blog/collections/articles:write&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;SINTER&lt;/tt&gt; retourne l'intersection de tous les sets passés en paramètres dans notre cas :
- &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;temp_perm:/buckets/blog/collections/articles:write&lt;/span&gt;&lt;/tt&gt;
- &lt;tt class="docutils literal"&gt;principals:fxa:alexis&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus d'informations sur :
- &lt;a class="reference external" href="http://redis.io/commands/sinter"&gt;http://redis.io/commands/sinter&lt;/a&gt;
- &lt;a class="reference external" href="http://redis.io/commands/sunionstore"&gt;http://redis.io/commands/sunionstore&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Si le set résultant de la commande &lt;tt class="docutils literal"&gt;SINTER&lt;/tt&gt; n'est pas vide, alors
l'utilisateur possède la permission.&lt;/p&gt;
&lt;p&gt;On peut ensuite supprimer la clé temporaire &lt;tt class="docutils literal"&gt;temp_perm&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;En utilisant &lt;tt class="docutils literal"&gt;MULTI&lt;/tt&gt; on peut &lt;a class="reference external" href="https://gist.github.com/Natim/77c8f61c1d42e476cef8#file-permission-py-L117-L124"&gt;même faire tout cela au sein d'une
transaction&lt;/a&gt;
et garantir ainsi l'intégrité de la requête.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;La solution a l'air simple mais nous a demandé beaucoup de réflexion
en passant par plusieurs propositions.&lt;/p&gt;
&lt;p&gt;L'idée finale est d'avoir :&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Un backend spécifique permettant de stocker les &lt;em&gt;principals&lt;/em&gt; des
utilisateurs et des &lt;em&gt;ACE&lt;/em&gt; (e.g. avec les sets Redis) ;&lt;/li&gt;
&lt;li&gt;La liste des principals read et write sur la table des objets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C'est dommage d'avoir le concept de permissions à deux endroits, mais
cela permet de connaître rapidement la permission d'un utilisateur sur
un objet et également de pouvoir récupérer tous les objets d'une
collection pour un utilisateur si celui-ci n'a pas accès à tous les
records de la collection, ou toutes les collections du bucket.&lt;/p&gt;
&lt;/div&gt;
</content></entry></feed>