mirror of
https://github.com/almet/notmyidea.git
synced 2025-05-01 13:02:23 +02:00
1294 lines
No EOL
104 KiB
XML
1294 lines
No EOL
104 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis Métaireau</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>2016-03-01T00:00:00+01:00</updated><entry><title>Service de nuages : Garantir l'intégrité des données via des signatures</title><link href="https://blog.notmyidea.org/service-de-nuages-garantir-lintegrite-des-donnees-via-des-signatures-fr.html" rel="alternate"></link><published>2016-03-01T00:00:00+01:00</published><updated>2016-03-01T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-03-01:/service-de-nuages-garantir-lintegrite-des-donnees-via-des-signatures-fr.html</id><summary type="html"><p class="first last">Comment garantir l'intégrité des données en utilisant les signatures.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla</em></p>
|
||
<p>Dans le cadre du projet <a class="reference external" href="https://wiki.mozilla.org/Firefox/Go_Faster">Go Faster</a>, nous souhaitons distribuer des
|
||
mises à jour de parties de <em>Firefox</em> de manière séparée des mises à jour majeures
|
||
(qui ont lieu toutes les 6 semaines).</p>
|
||
<p>Les données que nous souhaitons mettre à jour sur les clients sont multiples.
|
||
Entre autres, nous souhaitons gérer <a class="reference external" href="https://blog.mozilla.org/security/2015/03/03/revoking-intermediate-certificates-introducing-onecrl/">la mise à jour des listes de révocation
|
||
(CRL) de certificats SSL</a>.</p>
|
||
<p>Il est évidemment nécessaire de s'assurer que les données qui sont téléchargées
|
||
sur les client sont légitimes : que personne ne tente d'invalider des
|
||
certificats alors qu'ils sont valides, et que l'ensemble des mises à jour sont
|
||
bel et bien récupérées sur le client.</p>
|
||
<p>La signature garantit qu'une mise à jour contient tous les enregistrements, mais il
|
||
est toujours possible de bloquer l'accès au service (par exemple avec le <em>china
|
||
great firewall</em>).</p>
|
||
<p>Ce mécanisme fonctionne pour les listes de certificats à révoquer, mais pas
|
||
uniquement. Nous comptons réutiliser ce même fonctionnement dans le futur pour
|
||
la mise à jour d'autres parties de Firefox, et vous pouvez également en tirer
|
||
parti pour d'autres cas d'utilisation.</p>
|
||
<p>Nous souhaitons utiliser <a class="reference external" href="https://kinto.readthedocs.org">Kinto</a> afin
|
||
de distribuer ces jeux de données. Un des avantages est que l'on peut
|
||
facilement <em>cacher</em> les collections derrière un CDN.</p>
|
||
<p>Par contre, nous ne souhaitons pas que les clients fassent
|
||
confiance aveuglément, ni au serveur Kinto, ni au CDN.</p>
|
||
<p>Effectivement, un attaquant, contrôlant l'un ou l'autre, pourrait
|
||
alors envoyer les mises à jour qu'il souhaite à l'ensemble des clients
|
||
ou supprimer des certificats révoqués. Imaginez le carnage !</p>
|
||
<p>Afin de résoudre ce problème, considérons les conditions suivantes:</p>
|
||
<ul class="simple">
|
||
<li>La personne qui a le pouvoir de mettre à jour les CRL (<em>l'updater</em>)
|
||
a accès à une cle de signature (ou mieux, <a class="reference external" href="https://fr.wikipedia.org/wiki/Hardware_Security_Module">un HSM</a>) qui lui permet de
|
||
signer la collection;</li>
|
||
<li>Le pendant public de ce certificat est stocké et distribué dans Firefox;</li>
|
||
<li>Le <em>hashing</em> et la <em>signature</em> sont faits côté client pour éviter certains
|
||
vecteurs d'attaque (si un attaquant a la main sur le serveur Kinto par
|
||
exemple).</li>
|
||
</ul>
|
||
<p>Le chiffrement à sens unique, aussi appellé <em>hashing</em> est un moyen de toujours
|
||
obtenir le même résultat à partir de la même entrée.</p>
|
||
<div class="section" id="premier-envoi-de-donnees-sur-kinto">
|
||
<h2>Premier envoi de données sur Kinto</h2>
|
||
<p>L'ensemble des données est récupéré depuis une source <em>sécurisée</em> puis mis dans
|
||
une collection JSON. Chaque élément contient un identifiant unique généré sur
|
||
le client.</p>
|
||
<p>Par exemple, un enregistrement peut ressembler à :</p>
|
||
<div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="o">:</span> <span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
|
||
<span class="s2">&quot;fingerprint&quot;</span><span class="o">:</span> <span class="s2">&quot;11:D5:D2:0A:9A:F8:D9:FC:23:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3&quot;</span><span class="p">}</span>
|
||
</pre></div>
|
||
<p>Le <em>hash</em> de la collection est ensuite calculé, signé puis envoyé au serveur
|
||
(voir plus bas pour les détails).</p>
|
||
<p>La signature est déportée sur un service qui ne s'occupe que de ça, puisque la
|
||
sécurité du certificat qui s'occupe des signatures est extrêmement importante.</p>
|
||
</div>
|
||
<div class="section" id="comment-verifier-l-integrite-des-donnees">
|
||
<h2>Comment vérifier l'intégrité des données ?</h2>
|
||
<p>Premièrement, il faut récupérer l'ensemble des enregistrements présents sur
|
||
le serveur, ainsi que le <em>hash</em> et la signature associée.</p>
|
||
<p>Ensuite, vérifier la signature du <em>hash</em>, pour s'assurer que celui-ci provient
|
||
bien d'un tiers de confiance.</p>
|
||
<p>Finalement, recalculer le <em>hash</em> localement et valider qu'il correspond bien à
|
||
celui qui a été signé.</p>
|
||
</div>
|
||
<div class="section" id="ajouter-de-nouvelles-donnees">
|
||
<h2>Ajouter de nouvelles données</h2>
|
||
<p>Pour l'ajout de nouvelles données, il est nécessaire de s'assurer que les
|
||
données que l'on a localement sont valides avant de faire quoi que ce soit
|
||
d'autre.</p>
|
||
<p>Une fois ces données validées, il suffit de procéder comme la première fois, et
|
||
d'envoyer à nouveau le <em>hash</em> de la collection au serveur.</p>
|
||
</div>
|
||
<div class="section" id="comment-calculer-ce-hash">
|
||
<h2>Comment calculer ce hash ?</h2>
|
||
<p>Pour calculer le <em>hash</em> de la collection, il est nécessaire :</p>
|
||
<ol class="arabic simple">
|
||
<li>D'ordonner l'ensemble des éléments de la collection (par leur id) ;</li>
|
||
<li>Pour chaque élément, sérialiser les champs qui nous intéressent (les
|
||
concaténer clé + valeur)</li>
|
||
<li>Calculer le <em>hash</em> depuis la sérialisation.</li>
|
||
</ol>
|
||
<p>Nous sommes encore incertains de la manière dont le hash va être calculé. Les <a class="reference external" href="https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41">JSON Web Signature</a> semblent
|
||
une piste intéressante. En attendant, une implementation naïve en python
|
||
pourrait ressembler à ceci :</p>
|
||
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">json</span>
|
||
<span class="kn">import</span> <span class="nn">hashlib</span>
|
||
|
||
<span class="n">data</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
|
||
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="s2">&quot;11:D5:D2:0A:9A:F8:D9:FC:23:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3&quot;</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;dded96b7-8f0d-8f8a-49a4-7f771b4c4bc4&quot;</span><span class="p">,</span>
|
||
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="s2">&quot;33:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3:11:D5:D2:0A:9A:F8:D9:FC&quot;</span><span class="p">}]</span>
|
||
|
||
<span class="n">m</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">()</span>
|
||
<span class="n">m</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
|
||
<span class="n">collection_hash</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</content><category term="Technologie"></category></entry><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></name></author><id>tag:blog.notmyidea.org,2015-07-07:/service-de-nuages-perspectives-pour-lete-fr.html</id><summary type="html"><p class="first last">Le travail en cours et les fonctionnalités à venir pour les prochains mois.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla</em></p>
|
||
<p>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 à <a class="reference external" href="http://www.openstreetmap.org/node/268148288#map=4/50.12/-122.95">Whistler, en Colombie Britannique au Canada</a> !</p>
|
||
<img alt="«All Hands» talk about Lego, by &#64;davidcrob - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-talks.jpg" />
|
||
<p>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 <a class="reference external" href="https://kinto.readthedocs.org">Kinto</a>.</p>
|
||
<p>Dans cet article, nous passons en revue les pistes que nous avons pour
|
||
les prochains mois.</p>
|
||
<div class="section" id="ateliers-et-promotion">
|
||
<h2>Ateliers et promotion</h2>
|
||
<p>Nicolas a présenté <a class="reference external" href="https://github.com/mozilla-services/kinto.js">Kinto.js</a> dans un atelier dédié, avec comme support de
|
||
présentation le <a class="reference external" href="http://kintojs.readthedocs.org/en/latest/tutorial/">tutorial d'introduction</a>.</p>
|
||
<p>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 <a class="reference external" href="https://kinto.dev.mozaws.net/v1/">serveur de dev effacé tous les jours</a>.</p>
|
||
<p>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.</p>
|
||
<p>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.</p>
|
||
<img alt="Kinto.js workshop - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-workshop.jpg" />
|
||
</div>
|
||
<div class="section" id="mozilla-payments">
|
||
<h2>Mozilla Payments</h2>
|
||
<p>Comme <a class="reference external" href="http://www.servicedenuages.fr/la-gestion-des-permissions">décrit précédemment</a>, nous avons mis en place un système de permissions pour répondre aux besoins de suivi des paiements et abonnements.</p>
|
||
<p>Pour ce projet, Kinto sera utilisé depuis une application Django, via un client Python.</p>
|
||
<p>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.</p>
|
||
<div class="section" id="a-venir">
|
||
<h3>À venir</h3>
|
||
<p>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.</p>
|
||
<img alt="Whistler Alta Lake - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-lake.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="firefox-os-et-stockage">
|
||
<h2>Firefox OS et stockage</h2>
|
||
<p>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 <a class="reference external" href="https://github.com/mozilla-services/msisdn-gateway">serveur d'identification BrowserID par SMS</a> et pour <a class="reference external" href="https://github.com/mozilla-services/loop-server">Firefox Hello</a>.</p>
|
||
<div class="section" id="in-app-sync">
|
||
<h3>In-App sync</h3>
|
||
<p>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 <a class="reference external" href="http://daybed.readthedocs.org/">Daybed</a>. Voici donc une belle opportunité à saisir !</p>
|
||
<p>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 :)</p>
|
||
<p>Le fait que <a class="reference external" href="https://github.com/daleharvey">Dale</a>, un des auteurs de <a class="reference external" href="http://pouchdb.com/">PouchDB</a> et <a class="reference external" href="https://github.com/michielbdejong">Michiel de Jong</a>, un des auteurs de <a class="reference external" href="http://remotestorage.io/">Remote Storage</a>, nous aient encouragés sur nos premiers pas nous a bien motivé !</p>
|
||
</div>
|
||
<div class="section" id="cut-the-rope">
|
||
<h3>Cut the Rope</h3>
|
||
<p>Kinto devrait être mis à profit pour synchroniser les paramètres et les scores
|
||
du <a class="reference external" href="http://mozilla.cuttherope.net/">jeu</a>. Un premier exercice et une première vitrine sympas !</p>
|
||
</div>
|
||
<div class="section" id="syncto">
|
||
<h3>« SyncTo »</h3>
|
||
<p><a class="reference external" href="https://docs.services.mozilla.com/storage/apis-1.5.html">Firefox Sync</a> 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.</p>
|
||
<p>L'implémentation du client en JavaScript est relativement complexe et date un peu maintenant.
|
||
Le code existant n'est pas vraiment portable dans <em>Firefox OS</em> et les tentatives de réécriture
|
||
n'ont pas abouti.</p>
|
||
<p>Nous souhaitons implémenter un pont entre <em>Kinto</em> et <em>Firefox Sync</em>, de manière
|
||
à pouvoir utiliser le client <em>Kinto.js</em>, 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.</p>
|
||
<p>Alexis a sauté sur l'occasion pour commencer l'écriture d'<a class="reference external" href="https://github.com/mozilla-services/syncclient">un client python pour Firefox Sync</a>, qui servira de brique de base pour l'écriture du service.</p>
|
||
</div>
|
||
<div class="section" id="cloud-storage">
|
||
<h3>Cloud Storage</h3>
|
||
<p>Eden Chuang et Sean Lee ont présenté les avancées sur l'intégration de services de stockages
|
||
distants (<em>DropBox, Baidu Yun</em>) dans <em>Firefox OS</em>. Actuellement, leur preuve de
|
||
concept repose sur <a class="reference external" href="https://fr.wikipedia.org/wiki/Filesystem_in_Userspace">FUSE</a>.</p>
|
||
<p>Nous avons évidemment en tête d'introduire la notion de fichiers attachés dans
|
||
<em>Kinto</em>, en implémentant la specification
|
||
<a class="reference external" href="https://tools.ietf.org/html/draft-dejong-remotestorage-05">*Remote Storage*</a>,
|
||
mais pour l'instant les cas d'utilisations ne se sont pas encore présentés officiellement.</p>
|
||
</div>
|
||
<div class="section" id="id2">
|
||
<h3>À venir</h3>
|
||
<p>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.</p>
|
||
<p>Nous sommes par ailleurs perpétuellement preneurs de vos retours — et bien
|
||
entendu de vos contributions — tant sur le code <a class="reference external" href="https://github.com/mozilla-services/kinto/">serveur</a>
|
||
que <a class="reference external" href="https://github.com/mozilla-services/kinto.js/">client</a> !</p>
|
||
<img alt="Firefox OS Cloud Storage Presentation - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-cloud-storage.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="contenus-applicatifs-de-firefox">
|
||
<h2>Contenus applicatifs de Firefox</h2>
|
||
<p>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. <em>règles de securité, dictionnaires, traductions, ...</em>) <a class="footnote-reference" href="#id4" id="id3">[1]</a>.</p>
|
||
<p>Il s'agit de données JSON et binaire qui doivent être versionnées et synchronisées par
|
||
les navigateurs (<em>lecture seule</em>).</p>
|
||
<p>Il y a plusieurs outils officiels qui existent pour gérer ça (<em>Balrog</em>, <em>Shavar</em>, ...),
|
||
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, <em>Kinto</em> est tout aussi pertinent !</p>
|
||
<table class="docutils footnote" frame="void" id="id4" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id3">[1]</a></td><td>La bonne nouvelle c'est que toutes les fonctionnalités <em>third-party</em> qui ont
|
||
été intégrées récemment vont redevenir des <em>add-ons</em> \o/.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<img alt="Landscape - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-landscape.jpg" />
|
||
</div>
|
||
<div class="section" id="awesome-bar">
|
||
<h2>Awesome bar</h2>
|
||
<p>L'équipe <em>Firefox Labs</em>, 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 <em>Awesome bar</em>, qui fusionnerait URL, historique et recherche.</p>
|
||
<p>Nous ne pouvons pas en dire beaucoup plus pour l'instant, mais les fonctionnalités
|
||
de collections d'enregistrements partagées entre utilisateurs de <em>Kinto</em>
|
||
correspondent parfaitement à ce qui est envisagé pour le futur du navigateur :)</p>
|
||
<div class="section" id="id5">
|
||
<h3>À venir</h3>
|
||
<p>Nous serons donc probablement amenés, avant de la fin de l'année, à introduire des
|
||
fonctionnalités d'indexation et de recherche <em>full-text</em> (comprendre <em>ElasticSearch</em>).
|
||
Cela rejoint nos plans précédents, puisque c'est quelque chose que nous avions dans
|
||
<em>Daybed</em>, et qui figurait sur notre feuille de route !</p>
|
||
<img alt="Firefox Labs Meeting - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-labs.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="browser-html">
|
||
<h2>Browser.html</h2>
|
||
<p>L'équipe <em>Recherche</em> explore les notions de plateforme, et travaille notamment
|
||
sur l'implémentation d'un navigateur en JS/HTML avec <em>React</em>:
|
||
<a class="reference external" href="https://github.com/mozilla/browser.html">browser.html</a></p>
|
||
<p><em>Kinto</em> correspond parfaitement aux attentes
|
||
de l'équipe pour synchroniser les données associées à un utilisateur.</p>
|
||
<p>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 à <em>Alexa.com Top 500</em> pour fournir la complétion d'URL sans
|
||
interroger le moteur de recherche.</p>
|
||
<p>L'exercice pourrait être poussé jusqu'à la synchronisation d'états <em>React</em>
|
||
entre périphériques (par exemple pour les onglets).</p>
|
||
<div class="section" id="id7">
|
||
<h3>À venir</h3>
|
||
<p>Si <em>browser.html</em> 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 <a class="reference external" href="http://www.w3.org/TR/WebCryptoAPI/">il y a plusieurs standards</a> !</p>
|
||
<p>Pour éviter d'interroger le serveur à intervalle régulier afin de synchroniser les
|
||
changements, l'introduction des <a class="reference external" href="https://w3c.github.io/push-api/">*push notifications*</a> semble assez naturelle.
|
||
Il s'agirait alors de la dernière pierre qui manque à l'édifice pour obtenir
|
||
un «<em>Mobile/Web backend as a service</em>» complet.</p>
|
||
<img alt="Roadmap - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-roadmap.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="conclusion">
|
||
<h2>Conclusion</h2>
|
||
<p>Nous sommes dans une situation idéale, puisque ce que nous avions imaginé
|
||
sur <a class="reference external" href="https://github.com/mozilla-services/kinto/wiki/Roadmap">notre feuille de route</a> correspond à ce qui nous est demandé par les
|
||
différentes équipes.</p>
|
||
<p>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 :)</p>
|
||
<img alt="Friday Night Party - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-top-roof.jpg" />
|
||
</div>
|
||
</content><category term="Technologie"></category></entry><entry><title>Service de nuages : Achievement unlocked</title><link href="https://blog.notmyidea.org/service-de-nuages-achievement-unlocked-fr.html" rel="alternate"></link><published>2015-06-01T00:00:00+02:00</published><updated>2015-06-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-06-01:/service-de-nuages-achievement-unlocked-fr.html</id><summary type="html"><p class="first last">Après près de 3 mois intensifs, nous venons de <em>tagguer</em> Cliquet 2.0
|
||
et Kinto 1.0 !</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla</em></p>
|
||
<p>Aujourd'hui, c'est jour de fête : nous venons de publier Cliquet <strong>2.0</strong>
|
||
<a class="footnote-reference" href="#id5" id="id1">[1]</a> et Kinto <strong>1.0</strong> <a class="footnote-reference" href="#id6" id="id2">[2]</a>.</p>
|
||
<blockquote class="epigraph">
|
||
<p>L'aboutissement de 3 années de R&amp;D !</p>
|
||
<p class="attribution">&mdash;Rémy</p>
|
||
</blockquote>
|
||
<p><a class="reference external" href="https://kinto.readthedocs.org/en/latest/">Kinto</a> est un service pour
|
||
stocker, synchroniser et partager des données arbitraires, attachées à un
|
||
compte Firefox (mais le système d'authentification est <em>pluggable</em>).</p>
|
||
<p><a class="reference external" href="https://cliquet.readthedocs.org/en/latest/">Cliquet</a> est une boite à outils pour faciliter l'implémentation de
|
||
micro-services HTTP tels que les APIs <em>REST</em> ayant des besoins de synchronisation.</p>
|
||
<p>Vous pouvez lire plus à propos des raisons qui nous ont poussé à proposer cette nouvelle solution
|
||
et de notre ambition sur <a class="reference external" href="http://www.servicedenuages.fr/eco-systeme-et-stockage-generique.html">http://www.servicedenuages.fr/eco-systeme-et-stockage-generique.html</a></p>
|
||
<p>Nous sommes fiers du travail que nous avons pu réaliser durant ces derniers
|
||
mois sur ces deux projets. Bien que la plupart du travail que nous ayons
|
||
réalisé pour le serveur de liste de lecture (Reading List) ait pu être utilisé,
|
||
beaucoup de parties ont été repensées et nous avons introduit des
|
||
fonctionnalités que l'on attendait depuis longtemps, comme la gestion des
|
||
permissions.</p>
|
||
<p>Bien sur, exactement comme après un ré-aménagement de salon, on ne peut
|
||
s'empêcher de voir toutes les choses qui doivent toujours être améliorées,
|
||
notamment sur la documentation et les performances.</p>
|
||
<p>On peut déjà entrevoir à quoi l'écosystème va ressembler, et c'est prometteur.
|
||
Il y a déjà un client JavaScript <a class="footnote-reference" href="#id7" id="id3">[3]</a> dont l'objectif est de synchroniser les
|
||
données locales du navigateur avec une instance de Kinto.</p>
|
||
<p>N'hésitez vraiment pas à nous solliciter pour discuter avec vous si vous avez
|
||
des problématiques proches : nous accueillons avec plaisir toutes sortes de
|
||
retours, que ce soit à propos du code, de la documentation, de la sécurité de
|
||
la solution ou de la manière de communiquer avec le monde extérieur. Si vous
|
||
souhaitez nous contacter, vous pouvez laisser un commentaire ici ou nous
|
||
contacter sur le canal <a class="reference external" href="irc://irc.mozilla.org/#storage">#storage</a> sur le réseau IRC de Mozilla.</p>
|
||
<p>Et ce n'est que le début ! Le futur se dessine dans notre feuille de route
|
||
<a class="footnote-reference" href="#id8" id="id4">[4]</a>.</p>
|
||
<table class="docutils footnote" frame="void" id="id5" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td><strong>Cliquet</strong> est une boite à outils pour faciliter l'implémentation de
|
||
microservices HTTP tels que les APIs <em>REST</em> ayant des besoins de
|
||
synchronisation.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="id6" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id2">[2]</a></td><td><strong>Kinto</strong> est un service pour stocker, synchroniser et partager des données
|
||
arbitraires, attachées à un compte Firefox (mais le système d'authentification
|
||
est <em>pluggable</em>).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="id7" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id3">[3]</a></td><td>Cliquetis, la bibliothèque JavaScript pour consommer l'API HTTP de Kinto —
|
||
<a class="reference external" href="https://github.com/mozilla-services/cliquetis">https://github.com/mozilla-services/cliquetis</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="id8" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id4">[4]</a></td><td>La feuille de route de Kinto: <a class="reference external" href="https://github.com/mozilla-services/kinto/wiki/roadmap">https://github.com/mozilla-services/kinto/wiki/roadmap</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</content><category term="Technologie"></category></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></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"><p class="first last">Comment faire pour stocker et interroger la base de données au sujet des permissions avec Kinto ?</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla</em></p>
|
||
<p><strong>tl;dr: On a maintenant un super système de permission mais comment faire pour stocker et interroger ces permissions de manière efficace ?</strong></p>
|
||
<div class="section" id="la-problematique">
|
||
<h2>La problématique</h2>
|
||
<p>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.</p>
|
||
<p>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.</p>
|
||
</div>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-utilisateur">
|
||
<h2>Obtenir la liste des &quot;principals&quot; d'un utilisateur</h2>
|
||
<p>Les <em>principals</em> de l'utilisateur correspondent à son <tt class="docutils literal">user_id</tt>
|
||
ainsi qu'à la liste des identifiants des groupes dans lesquels il a
|
||
été ajouté.</p>
|
||
<p>Pour éviter de recalculer les <em>principals</em> de l'utilisateur à chaque
|
||
requête, le mieux reste de maintenir une liste des <em>principals</em> par
|
||
utilisateur.</p>
|
||
<p>Ainsi lorsqu'on ajoute un utilisateur à un groupe, il faut bien penser
|
||
à ajouter le groupe à la liste des <em>principals</em> de l'utilisateur.</p>
|
||
<p>Ça se complexifie lorsqu'on ajoute un groupe à un groupe.</p>
|
||
<p>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.</p>
|
||
<p>L'avantage de maintenir la liste des <em>principals</em> 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.</p>
|
||
<p>Cela nécessite de donner un identifiant unique aux groupes pour tous
|
||
les <em>buckets</em>.</p>
|
||
<p>Nous proposons de de les nommer avec leur URI:
|
||
<tt class="docutils literal">/buckets/blog/groups/moderators</tt></p>
|
||
</div>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-ace">
|
||
<h2>Obtenir la liste des &quot;principals&quot; d'un ACE</h2>
|
||
<blockquote>
|
||
Rappel, un &quot;ACE&quot; est un <em>Access Control Entry</em>, un des éléments
|
||
d'une ACL (e.g. <em>modifier un enregistrement</em>).</blockquote>
|
||
<p>Avec le <a class="reference external" href="https://blog.notmyidea.org/service-de-nuages-la-gestion-des-permissions-fr.html">système de permissions choisi</a>, les permissions d'un
|
||
objet héritent de celle de l'objet parent.</p>
|
||
<p>Par exemple, avoir le droit d'écriture sur un <em>bucket</em> permet la
|
||
création des permissions et la modification de tous ses records.</p>
|
||
<p>Ce qui veut dire que pour obtenir la liste complète des <em>principals</em>
|
||
ayant une permission sur un objet, il faut regarder à plusieurs
|
||
endroits.</p>
|
||
<p>Rémy a <a class="reference external" href="https://gist.github.com/Natim/77c8f61c1d42e476cef8#file-permission-py-L9-L52">décrit dans un gist la liste d'héritage de chaque permission</a>.</p>
|
||
<p>Prenons l'exemple de l'ajout d'un record dans une collection.</p>
|
||
<p>Le droit <tt class="docutils literal">records:create</tt> est obtenu si l'on a l'un des droits suivants:</p>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal">bucket:write</tt></li>
|
||
<li><tt class="docutils literal">collection:write</tt></li>
|
||
<li><tt class="docutils literal">records:create</tt></li>
|
||
</ul>
|
||
<p>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. (<em>Je vous laisse imaginer le nombre d'opérations
|
||
nécessaires pour ajouter un administrateur sur un *bucket</em> contenant
|
||
1000 collections avec 100000 records chacune.*)</p>
|
||
<p>La solution que nous avons désormais adoptée consiste à stocker les
|
||
<em>principals</em> de chaque <em>ACE</em> (<em>qui</em> a le droit de faire telle action
|
||
sur l'objet), et de faire l'union des <em>ACE</em> hérités, afin de les
|
||
croiser avec les <em>principals</em> de l'utilisateur :</p>
|
||
<blockquote>
|
||
(ACE(object, permission) ∪ inherited_ACE) ∩ PRINCIPALS(user)</blockquote>
|
||
<p>Par exemple l'ACE: <tt class="docutils literal">/buckets/blog/collections/article:records:create</tt> hérite de
|
||
l'ACE <tt class="docutils literal">/buckets/blog/collections/article:write</tt> et de <tt class="docutils literal">/buckets/blog:write</tt> :</p>
|
||
<blockquote>
|
||
(ACE(/buckets/blog/collections/article:records:create) ∪ ACE(/buckets/blog/collections/article:write) ∪ ACE(/buckets/blog:write)) ∩ PRINCIPALS('fxa:alexis')</blockquote>
|
||
</div>
|
||
<div class="section" id="recuperer-les-donnees-de-l-utilisateur">
|
||
<h2>Récupérer les données de l'utilisateur</h2>
|
||
<p>La situation se corse lorsqu'on souhaite limiter la liste des
|
||
<em>records</em> d'une collection à ceux accessibles pour l'utilisateur, car
|
||
on doit faire cette intersection pour tous les <em>records</em>.</p>
|
||
<p>Une première solution est de regarder si l'utilisateur est mentionné
|
||
dans les <em>ACL*s du *bucket</em> ou de la <em>collection</em>:</p>
|
||
<p>Ensuite, si ce n'est pas le cas, alors on filtre les <em>records</em> pour
|
||
lesquels les <em>principals</em> correspondent à ceux de l'utilisateur.</p>
|
||
<div class="highlight"><pre><span></span><span class="n">principals</span> <span class="o">=</span> <span class="n">get_user_principals</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
||
<span class="n">can_read_all</span> <span class="o">=</span> <span class="n">has_read_perms</span><span class="p">(</span><span class="n">bucket_id</span><span class="p">,</span> <span class="n">collection_id</span><span class="p">,</span>
|
||
<span class="n">principals</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">can_read_all</span><span class="p">:</span>
|
||
<span class="n">records</span> <span class="o">=</span> <span class="n">get_all_records</span><span class="p">(</span><span class="n">bucket_id</span><span class="p">,</span> <span class="n">collection_id</span><span class="p">,</span>
|
||
<span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">])</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">records</span> <span class="o">=</span> <span class="n">filter_read_records</span><span class="p">(</span><span class="n">bucket_id</span><span class="p">,</span> <span class="n">collection_id</span><span class="p">,</span>
|
||
<span class="n">principals</span><span class="o">=</span><span class="n">principals</span><span class="p">,</span>
|
||
<span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">])</span>
|
||
</pre></div>
|
||
<p>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.</p>
|
||
</div>
|
||
<div class="section" id="le-modele-de-donnees">
|
||
<h2>Le modèle de données</h2>
|
||
<p>Pour avoir une idée des requêtes dans un backend SQL, voyons un peu ce
|
||
que donnerait le modèle de données.</p>
|
||
<div class="section" id="le-format-des-id">
|
||
<h3>Le format des ID</h3>
|
||
<p>Utiliser des URI comme identifiant des objets présente de nombreux
|
||
avantages (lisibilité, unicité, cohérence avec les URLs)</p>
|
||
<ul class="simple">
|
||
<li>bucket: <tt class="docutils literal">/buckets/blog</tt></li>
|
||
<li>groupe: <tt class="docutils literal">/buckets/blog/group/moderators</tt></li>
|
||
<li>collection: <tt class="docutils literal">/buckets/blog/collections/articles</tt></li>
|
||
<li>record: <tt class="docutils literal"><span class="pre">/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200</span></tt></li>
|
||
</ul>
|
||
</div>
|
||
<div class="section" id="les-tables">
|
||
<h3>Les tables</h3>
|
||
<p>Pour le stockage des principals et des permissions:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">user</span><span class="p">(</span><span class="n">id</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="n">principals</span> <span class="nb">TEXT</span><span class="p">[]);</span>
|
||
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">perms</span><span class="p">(</span><span class="n">ace</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="n">principals</span> <span class="nb">TEXT</span><span class="p">[]);</span>
|
||
</pre></div>
|
||
<p>La table <em>perms</em> va associer des <em>principals</em> à chaque <em>ACE</em>
|
||
(e.g.``/buckets/blog:write``).</p>
|
||
<p>Pour le stockage des données:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">object</span><span class="p">(</span><span class="n">id</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="k">type</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="n">parent_id</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="k">data</span> <span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="n">write_principals</span> <span class="nb">TEXT</span><span class="p">[],</span> <span class="n">read_principals</span> <span class="nb">TEXT</span><span class="p">[]);</span>
|
||
</pre></div>
|
||
<p>La colonne <em>parent_id</em> permet de savoir à qui appartient l'objet
|
||
(e.g. groupe d'un <em>bucket</em>, collection d'un <em>bucket</em>, <em>record</em> d'une
|
||
collection, ...).</p>
|
||
</div>
|
||
<div class="section" id="exemple-d-utilisateur">
|
||
<h3>Exemple d'utilisateur</h3>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">user</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span> <span class="p">(</span><span class="s1">&#39;fxa:alexis&#39;</span><span class="p">,</span> <span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
|
||
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">user</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span> <span class="p">(</span><span class="s1">&#39;fxa:natim&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;/buckets/blog/groups/moderators&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="exemple-d-objets">
|
||
<h3>Exemple d'objets</h3>
|
||
<div class="section" id="bucket">
|
||
<h4>Bucket</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">object</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">parent_id</span><span class="p">,</span> <span class="k">data</span><span class="p">,</span>
|
||
<span class="n">read_principals</span><span class="p">,</span> <span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span> <span class="p">(</span>
|
||
<span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;bucket&#39;</span><span class="p">,</span>
|
||
<span class="k">NULL</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;name&quot;: &quot;blog&quot;}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="s1">&#39;{}&#39;</span><span class="p">,</span> <span class="s1">&#39;{&quot;fxa:alexis&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="group">
|
||
<h4>Group</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">object</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">parent_id</span><span class="p">,</span> <span class="k">data</span><span class="p">,</span>
|
||
<span class="n">read_principals</span><span class="p">,</span> <span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span> <span class="p">(</span>
|
||
<span class="s1">&#39;/buckets/blog/groups/moderators&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;group&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;name&quot;: &quot;moderators&quot;, &quot;members&quot;: [&#39;</span><span class="n">fxa</span><span class="p">:</span><span class="n">natim</span><span class="s1">&#39;]}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="s1">&#39;{}&#39;</span><span class="p">,</span> <span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
<p>Ce groupe peut être gére par <tt class="docutils literal">fxa:alexis</tt> puisqu'il a la permission
|
||
<tt class="docutils literal">write</tt> dans le <em>bucket</em> parent.</p>
|
||
</div>
|
||
<div class="section" id="collection">
|
||
<h4>Collection</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">object</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">parent_id</span><span class="p">,</span> <span class="k">data</span><span class="p">,</span>
|
||
<span class="n">read_principals</span><span class="p">,</span> <span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span> <span class="p">(</span>
|
||
<span class="s1">&#39;/buckets/blog/collections/articles&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;collection&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;name&quot;: &quot;article&quot;}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;system.Everyone&quot;}&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;/buckets/blog/groups/moderators&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
<p>Cette collection d'articles peut être lue par tout le monde,
|
||
et gérée par les membres du groupe <tt class="docutils literal">moderators</tt>, ainsi que
|
||
<tt class="docutils literal">fxa:alexis</tt>, via le <em>bucket</em>.</p>
|
||
</div>
|
||
<div class="section" id="records">
|
||
<h4>Records</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">object</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">parent_id</span><span class="p">,</span> <span class="k">data</span><span class="p">,</span>
|
||
<span class="n">read_principals</span><span class="p">,</span> <span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span> <span class="p">(</span>
|
||
<span class="s1">&#39;/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;record&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;/buckets/blog/collections/articles&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;{&quot;name&quot;: &quot;02f3f76f-7059-4ae4-888f-2ac9824e9200&quot;,</span>
|
||
<span class="s1"> &quot;title&quot;: &quot;Stocker les permissions&quot;, ...}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="s1">&#39;{}&#39;</span><span class="p">,</span> <span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="interroger-les-permissions">
|
||
<h3>Interroger les permissions</h3>
|
||
<div class="section" id="id1">
|
||
<h4>Obtenir la liste des &quot;principals&quot; d'un ACE</h4>
|
||
<p>Comme vu plus haut, pour vérifier une permission, on fait l'union des
|
||
<em>principals</em> requis par les objets hérités, et on teste leur
|
||
intersection avec ceux de l'utilisateur:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">WITH</span> <span class="n">required_principals</span> <span class="k">AS</span> <span class="p">(</span>
|
||
<span class="k">SELECT</span> <span class="k">unnest</span><span class="p">(</span><span class="n">principals</span><span class="p">)</span> <span class="k">AS</span> <span class="n">p</span>
|
||
<span class="k">FROM</span> <span class="n">perms</span>
|
||
<span class="k">WHERE</span> <span class="n">ace</span> <span class="k">IN</span> <span class="p">(</span>
|
||
<span class="s1">&#39;/buckets/blog:write&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;/buckets/blog:read&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;/buckets/blog/collections/article:write&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;/buckets/blog/collections/article:read&#39;</span><span class="p">)</span>
|
||
<span class="p">),</span>
|
||
<span class="n">user_principals</span> <span class="k">AS</span> <span class="p">(</span>
|
||
<span class="k">SELECT</span> <span class="k">unnest</span><span class="p">(</span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="k">FROM</span> <span class="k">user</span>
|
||
<span class="k">WHERE</span> <span class="n">id</span> <span class="o">=</span> <span class="s1">&#39;fxa:natim&#39;</span>
|
||
<span class="p">)</span>
|
||
<span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
|
||
<span class="k">FROM</span> <span class="n">user_principals</span> <span class="n">a</span>
|
||
<span class="k">INNER</span> <span class="k">JOIN</span> <span class="n">required_principals</span> <span class="n">b</span>
|
||
<span class="k">ON</span> <span class="n">a</span><span class="p">.</span><span class="n">p</span> <span class="o">=</span> <span class="n">b</span><span class="p">.</span><span class="n">p</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="filtrer-les-objets-en-fonction-des-permissions">
|
||
<h4>Filtrer les objets en fonction des permissions</h4>
|
||
<p>Pour filtrer les objets, on fait une simple intersection de liste
|
||
(<em>merci PostgreSQL</em>):</p>
|
||
<div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="k">data</span>
|
||
<span class="k">FROM</span> <span class="k">object</span> <span class="n">o</span><span class="p">,</span> <span class="k">user</span> <span class="n">u</span>
|
||
<span class="k">WHERE</span> <span class="n">o</span><span class="p">.</span><span class="k">type</span> <span class="o">=</span> <span class="s1">&#39;record&#39;</span>
|
||
<span class="k">AND</span> <span class="n">o</span><span class="p">.</span><span class="n">parent_id</span> <span class="o">=</span> <span class="s1">&#39;/buckets/blog/collections/article&#39;</span>
|
||
<span class="k">AND</span> <span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">read_principals</span> <span class="o">&amp;&amp;</span> <span class="n">u</span><span class="p">.</span><span class="n">principals</span> <span class="k">OR</span>
|
||
<span class="n">o</span><span class="p">.</span><span class="n">write_principals</span> <span class="o">&amp;&amp;</span> <span class="n">u</span><span class="p">.</span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="k">AND</span> <span class="n">u</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="s1">&#39;fxa:natim&#39;</span><span class="p">;</span>
|
||
</pre></div>
|
||
<p>Les listes s'indexent bien, notamment grâce aux <a class="reference external" href="http://www.postgresql.org/docs/current/static/indexes-types.html">index GIN</a>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="avec-redis">
|
||
<h3>Avec Redis</h3>
|
||
<p><em>Redis</em> présente plusieurs avantages pour ce genre de
|
||
problématiques. Notamment, il gère les <em>set</em> nativement (listes de
|
||
valeurs uniques), ainsi que les opérations d'intersection et d'union.</p>
|
||
<p>Avec <em>Redis</em> on peut écrire l'obtention des <em>principals</em> pour un <em>ACE</em>
|
||
comme cela :</p>
|
||
<div class="highlight"><pre><span></span>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
|
||
</pre></div>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal">SUNIONSTORE</tt> permet de créer un set contenant les éléments de
|
||
l'union de tous les set suivants. Dans notre cas on le nomme
|
||
<tt class="docutils literal"><span class="pre">temp_perm:/buckets/blog/collections/articles:write</span></tt> et il contient
|
||
l'union des sets d'ACLs suivants:
|
||
- <tt class="docutils literal"><span class="pre">permission:/buckets/blog:write</span></tt>
|
||
- <tt class="docutils literal"><span class="pre">permission:/buckets/blog/collections/articles:write</span></tt></li>
|
||
<li><tt class="docutils literal">SINTER</tt> retourne l'intersection de tous les sets passés en paramètres dans notre cas :
|
||
- <tt class="docutils literal"><span class="pre">temp_perm:/buckets/blog/collections/articles:write</span></tt>
|
||
- <tt class="docutils literal">principals:fxa:alexis</tt></li>
|
||
</ul>
|
||
<p>Plus d'informations sur :
|
||
- <a class="reference external" href="http://redis.io/commands/sinter">http://redis.io/commands/sinter</a>
|
||
- <a class="reference external" href="http://redis.io/commands/sunionstore">http://redis.io/commands/sunionstore</a></p>
|
||
<p>Si le set résultant de la commande <tt class="docutils literal">SINTER</tt> n'est pas vide, alors
|
||
l'utilisateur possède la permission.</p>
|
||
<p>On peut ensuite supprimer la clé temporaire <tt class="docutils literal">temp_perm</tt>.</p>
|
||
<p>En utilisant <tt class="docutils literal">MULTI</tt> on peut <a class="reference external" href="https://gist.github.com/Natim/77c8f61c1d42e476cef8#file-permission-py-L117-L124">même faire tout cela au sein d'une
|
||
transaction</a>
|
||
et garantir ainsi l'intégrité de la requête.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="conclusion">
|
||
<h2>Conclusion</h2>
|
||
<p>La solution a l'air simple mais nous a demandé beaucoup de réflexion
|
||
en passant par plusieurs propositions.</p>
|
||
<p>L'idée finale est d'avoir :</p>
|
||
<ul class="simple">
|
||
<li>Un backend spécifique permettant de stocker les <em>principals</em> des
|
||
utilisateurs et des <em>ACE</em> (e.g. avec les sets Redis) ;</li>
|
||
<li>La liste des principals read et write sur la table des objets.</li>
|
||
</ul>
|
||
<p>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.</p>
|
||
</div>
|
||
</content><category term="Technologie"></category></entry><entry><title>Service de nuages : La gestion des permissions</title><link href="https://blog.notmyidea.org/service-de-nuages-la-gestion-des-permissions-fr.html" rel="alternate"></link><published>2015-05-01T00:00:00+02:00</published><updated>2015-05-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-05-01:/service-de-nuages-la-gestion-des-permissions-fr.html</id><summary type="html"><p class="first last">Démystification du vocabulaire des permissions et proposition d'implémentation pour Kinto</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla</em></p>
|
||
<p>Dans le cadre de la création d'un service de stockage de données personnelles
|
||
(Kinto), la gestion des permissions est un des gros challenges : qui doit avoir
|
||
accès à quoi, et comment le définir ?</p>
|
||
<p><strong>tl;dr: Quelques retours sur le vocabulaire des systèmes de permission et sur nos idées pour l'implementation des permissions dans un stockage générique.</strong></p>
|
||
<div class="section" id="la-problematique">
|
||
<h2>La problématique</h2>
|
||
<p>La problématique est simple : des données sont stockées en ligne, et il
|
||
faut un moyen de pouvoir les partager avec d'autres personnes.</p>
|
||
<p>En regardant les cas d'utilisations, on se rend compte qu'on a plusieurs types
|
||
d'utilisateurs :</p>
|
||
<ul class="simple">
|
||
<li>les utilisateurs &quot;finaux&quot; (vous) ;</li>
|
||
<li>les applications qui interagissent en leurs noms.</li>
|
||
</ul>
|
||
<p>Tous les intervenants n'ont donc pas les mêmes droits : certains doivent
|
||
pouvoir lire, d'autres écrire, d'autres encore créer de nouveaux
|
||
enregistrements, et le contrôle doit pouvoir s'effectuer de manière fine : il
|
||
doit être possible de lire un enregistrement mais pas un autre, par exemple.</p>
|
||
<p>Nous sommes partis du constat que les solutions disponibles n'apportaient pas
|
||
une réponse satisfaisante à ces besoins.</p>
|
||
</div>
|
||
<div class="section" id="un-probleme-de-vocabulaire">
|
||
<h2>Un problème de vocabulaire</h2>
|
||
<p>Le principal problème rencontré lors des réflexions fût le vocabulaire.</p>
|
||
<p>Voici ci-dessous une explication des différents termes.</p>
|
||
<div class="section" id="le-concept-de-principal">
|
||
<h3>Le concept de « principal »</h3>
|
||
<p>Un <em>principal</em>, en sécurité informatique, est une entité qui peut être
|
||
authentifiée par un système informatique. <a class="footnote-reference" href="#id2" id="id1">[1]</a> En Français il s'agit
|
||
du « commettant », l'acteur qui commet l'action (oui, le terme est conceptuel !)</p>
|
||
<p>Il peut s'agir aussi bien d'un individu, d'un ordinateur, d'un
|
||
service ou d'un groupe regroupant l'une de ces entités, ce qui
|
||
est plus large que le classique « <em>user id</em> ».</p>
|
||
<p>Les permissions sont alors associées à ces <em>principals</em>.</p>
|
||
<p>Par exemple, un utilisateur est identifié de manière unique lors de la
|
||
connexion par le système d'authentification dont le rôle est de
|
||
définir une liste de <em>principals</em> pour l'utilisateur se connectant.</p>
|
||
<table class="docutils footnote" frame="void" id="id2" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td>Pour en savoir plus sur les <em>principals</em> :
|
||
<a class="reference external" href="https://en.wikipedia.org/wiki/Principal_%28computer_security%29">https://en.wikipedia.org/wiki/Principal_%28computer_security%29</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="la-difference-entre-role-et-groupe">
|
||
<h3>La différence entre rôle et groupe</h3>
|
||
<p>De but en blanc, il n'est pas évident de définir précisément la
|
||
différence entre ces deux concepts qui permettent d'associer
|
||
des permissions à un groupe de <em>principals</em>. <a class="footnote-reference" href="#id4" id="id3">[2]</a></p>
|
||
<p>La différence est principalement sémantique. Mais on peut y voir une
|
||
différence dans la « direction » de la relation entre les deux concepts.</p>
|
||
<ul class="simple">
|
||
<li>Un rôle est une liste de permissions que l'on associe à un <em>principal</em>.</li>
|
||
<li>Un groupe est une liste de <em>principals</em> que l'on peut associer à une permission.</li>
|
||
</ul>
|
||
<table class="docutils footnote" frame="void" id="id4" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td>Plus d'informations :
|
||
<a class="reference external" href="http://stackoverflow.com/questions/7770728/group-vs-role-any-real-difference">http://stackoverflow.com/questions/7770728/group-vs-role-any-real-difference</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="la-difference-entre-permission-acl-ace">
|
||
<h3>La différence entre permission, ACL, ACE</h3>
|
||
<blockquote class="epigraph">
|
||
<p>Une ACL est une liste d’Access Control Entry (ACE) ou entrée de contrôle d'accès
|
||
donnant ou supprimant des droits d'accès à une personne ou un groupe.</p>
|
||
<p class="attribution">&mdash;<a class="reference external" href="https://fr.wikipedia.org/wiki/Access_Control_List">https://fr.wikipedia.org/wiki/Access_Control_List</a></p>
|
||
</blockquote>
|
||
<p>Je dirais même plus, dans notre cas, « à un <em>principal</em> ». Par exemple:</p>
|
||
<div class="highlight"><pre><span></span>create_record: alexis,remy,tarek
|
||
</pre></div>
|
||
<p>Cet ACE donne la permission <tt class="docutils literal">create</tt> sur l'objet <tt class="docutils literal">record</tt> aux
|
||
utilisateurs Tarek, Rémy et Alexis.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="la-delegation-de-permissions">
|
||
<h2>La délégation de permissions</h2>
|
||
<p>Imaginez l'exemple suivant, où un utilisateur stocke deux types de données en
|
||
ligne :</p>
|
||
<ul class="simple">
|
||
<li>des contacts ;</li>
|
||
<li>une liste de tâches à faire qu'il peut associer à ses contacts.</li>
|
||
</ul>
|
||
<p>L'utilisateur a tous les droits sur ses données.</p>
|
||
<p>Cependant il utilise deux applications qui doivent elles avoir un accès restreint :</p>
|
||
<ul class="simple">
|
||
<li>une application de gestion des contacts à qui il souhaite déléguer
|
||
la gestion intégrale de ses contacts : <tt class="docutils literal">contacts:write</tt> ;</li>
|
||
<li>une application de gestion des tâches à qui il souhaite déléguer la
|
||
gestion des tâches : <tt class="docutils literal">contacts:read tasks:write</tt></li>
|
||
</ul>
|
||
<p>Il souhaite que son application de contacts ne puisse pas accéder à
|
||
ses tâches et que son application de tâches ne puisse pas modifier ses
|
||
contacts existants, juste éventuellement en créer de nouveaux.</p>
|
||
<p>Il lui faut donc un moyen de déléguer certains de ses droits à un tiers
|
||
(l'application).</p>
|
||
<p>C'est précisément le rôle des <a class="reference external" href="http://tools.ietf.org/html/rfc6749#page-23">scopes OAuth2</a>.</p>
|
||
<p>Lors de la connexion d'un utilisateur, une fenêtre lui demande quels
|
||
accès il veut donner à l'application qui va agir en son nom.</p>
|
||
<p>Le service aura ensuite accès à ces <em>scopes</em> en regardant le jeton
|
||
d'authentification utilisé. Cette information doit être
|
||
considérée comme une entrée utilisateur (c'est à dire qu'on ne peut
|
||
pas lui faire confiance). Il s'agit de ce que l'utilisateur souhaite.</p>
|
||
<p>Or, il est également possible que l'utilisateur n'ait pas accès aux données
|
||
qu'il demande. Le service doit donc utiliser deux niveaux de permissions :
|
||
celles de l'utilisateur, et celles qui ont été déléguées à l'application.</p>
|
||
</div>
|
||
<div class="section" id="espace-de-noms">
|
||
<h2>Espace de noms</h2>
|
||
<p>Dans notre implémentation initiale de <em>Kinto</em> (notre service de stockage en
|
||
construction), l'espace de nom était implicite : les données stockées étaient
|
||
nécessairement celles de l'utilisateur connecté.</p>
|
||
<p>Les données d'un utilisateur étaient donc cloisonnées et impossible à partager.</p>
|
||
<p>L'utilisation d'espaces de noms est une manière simple de gérer le partage des données.</p>
|
||
<p>Nous avons choisi de reprendre le modèle de GitHub et de Bitbucket, qui
|
||
utilisent les « organisations » comme espaces de noms.</p>
|
||
<p>Dans notre cas, il est possible de créer des &quot;buckets&quot;, qui correspondent à ces
|
||
espaces de noms. Un bucket est un conteneur de collections et de groupes
|
||
utilisateurs.</p>
|
||
<p>Les ACLs sur ces collections peuvent être attribuées à certains groupes du
|
||
<em>bucket</em> ainsi qu'à d'autres <em>principals</em> directement.</p>
|
||
</div>
|
||
<div class="section" id="notre-proposition-d-api">
|
||
<h2>Notre proposition d'API</h2>
|
||
<div class="section" id="les-objets-manipules">
|
||
<h3>Les objets manipulés</h3>
|
||
<p>Pour mettre en place la gestion des permissions, nous avons identifié les
|
||
objets suivants :</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="23%" />
|
||
<col width="77%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><strong>bucket</strong></td>
|
||
<td>On peut les voir comme des espaces de noms. Ils
|
||
permettent d'avoir différentes collections portant
|
||
le même nom mais stockées dans différents <em>buckets</em> de
|
||
manière à ce que les données soient distinctes.</td>
|
||
</tr>
|
||
<tr><td><strong>collection</strong></td>
|
||
<td>Une liste d'enregistrements.</td>
|
||
</tr>
|
||
<tr><td><strong>record</strong></td>
|
||
<td>Un enregistrement d'une collection.</td>
|
||
</tr>
|
||
<tr><td><strong>group</strong></td>
|
||
<td>Un groupe de commetants (« <em>principals</em> »).</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Pour la définition des ACLs, il y a une hiérarchie et les objets « héritent » des
|
||
ACLs de leur parents :</p>
|
||
<div class="highlight"><pre><span></span> +---------------+
|
||
| Bucket |
|
||
+---------------+
|
||
+-----&gt;+ - id +&lt;---+
|
||
| | - permissions | |
|
||
| +---------------+ |
|
||
| |
|
||
| |
|
||
| |
|
||
| |
|
||
| |
|
||
+---+-----------+ +------+---------+
|
||
| Collection | | Group |
|
||
+---------------+ +----------------+
|
||
| - id | | - id |
|
||
| - permissions | | - members |
|
||
+------+--------+ | - permissions |
|
||
^ +----------------+
|
||
|
|
||
|
|
||
+------+---------+
|
||
| Record |
|
||
+----------------+
|
||
| - id |
|
||
| - data |
|
||
| - permissions |
|
||
+----------------+
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="les-permissions">
|
||
<h3>Les permissions</h3>
|
||
<p>Pour chacun de ces objets nous avons identifié les permissions suivantes :</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="23%" />
|
||
<col width="77%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Permission</th>
|
||
<th class="head">Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><strong>read</strong></td>
|
||
<td>La permission de lire le contenu de
|
||
l'objet et de ses sous-objets.</td>
|
||
</tr>
|
||
<tr><td><strong>write</strong></td>
|
||
<td>La permission de modifier et
|
||
d'administrer un objet et ses sous-
|
||
objets. La permission <em>write</em> permet la
|
||
lecture, modification et suppression
|
||
d'un objet ainsi que la gestion de ses
|
||
permissions.</td>
|
||
</tr>
|
||
<tr><td><strong>create</strong></td>
|
||
<td>La permission de créer le sous-objet
|
||
spécifié. Par exemple:
|
||
<tt class="docutils literal">collections:create</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>À chaque permission spécifiée sur un objet est associée une liste de
|
||
<em>principals</em>.</p>
|
||
<p>Dans le cas de la permission <tt class="docutils literal">create</tt> on est obligé de spécifier
|
||
l'objet enfant en question car un objet peut avoir plusieurs types
|
||
d'enfants. Par exemple : <tt class="docutils literal">collections:create</tt>, <tt class="docutils literal">groups:create</tt>.</p>
|
||
<p>Nous n'avons pour l'instant pas de permission pour <cite>delete</cite> et <cite>update</cite>,
|
||
puisque nous n'avons pas trouvé de cas d'utilisation qui les nécessitent.
|
||
Quiconque avec le droit d'écriture peut donc supprimer un enregistrement.</p>
|
||
<p>Les permissions d'un objet sont héritées de son parent. Par exemple,
|
||
un enregistrement créé dans une collection accessible à tout le monde
|
||
en lecture sera lui aussi accessible à tout le monde.</p>
|
||
<p>Par conséquent, les permissions sont cumulées. Autrement dit, il n'est pas
|
||
possible qu'un objet ait des permissions plus restrictives que son parent.</p>
|
||
<p>Voici la liste exhaustive des permissions :</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="21%" />
|
||
<col width="32%" />
|
||
<col width="47%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions associées</th>
|
||
<th class="head">Commentaire</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td>Configuration
|
||
(.ini)</td>
|
||
<td><cite>buckets:create</cite></td>
|
||
<td>Les <em>principals</em> ayant le droit
|
||
de créer un bucket sont définis
|
||
dans la configuration du serveur.
|
||
(<em>ex. utilisateurs authentifiés</em>)</td>
|
||
</tr>
|
||
<tr><td rowspan="4"><tt class="docutils literal">bucket</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>C'est en quelque sorte le droit
|
||
d'administration du <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>C'est le droit de lire le contenu
|
||
de tous les objets du <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>collections:create</cite></td>
|
||
<td>Permission de créer des
|
||
collections dans le <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>groups:create</cite></td>
|
||
<td>Permission de créer des groupes
|
||
dans le <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td rowspan="3"><tt class="docutils literal">collection</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission d'administrer tous les
|
||
objets de la collection.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter tous les
|
||
objets de la collection.</td>
|
||
</tr>
|
||
<tr><td><cite>records:create</cite></td>
|
||
<td>Permission de créer des nouveaux
|
||
enregistrement dans la collection.</td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">record</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission de modifier ou de
|
||
partager l'enregistrement.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter
|
||
l'enregistrement.</td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">group</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission d'administrer le
|
||
groupe</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter les
|
||
membres du groupe.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="les-principals">
|
||
<h3>Les « <em>principals</em> »</h3>
|
||
<p>Les acteurs se connectant au service de stockage peuvent s'authentifier.</p>
|
||
<p>Ils reçoivent alors une liste de <em>principals</em> :</p>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal">Everyone</tt>: le <em>principal</em> donné à tous les acteurs (authentifiés ou pas) ;</li>
|
||
<li><tt class="docutils literal">Authenticated</tt>: le <em>principal</em> donné à tous les acteurs authentifiés ;</li>
|
||
<li>un <em>principal</em> identifiant l'acteur, par exemple <tt class="docutils literal">fxa:32aa95a474c984d41d395e2d0b614aa2</tt></li>
|
||
</ul>
|
||
<p>Afin d'éviter les collisions d'identifiants, le <em>principal</em> de l'acteur dépend
|
||
de son type d'authentification (<tt class="docutils literal">system</tt>, <tt class="docutils literal">basic</tt>, <tt class="docutils literal">ipaddr</tt>, <tt class="docutils literal">hawk</tt>,
|
||
<tt class="docutils literal">fxa</tt>) et de son identifiant (unique par acteur).</p>
|
||
<p>En fonction du <em>bucket</em> sur lequel se passe l'action, les groupes dont
|
||
fait partie l'utilisateur sont également ajoutés à sa liste de
|
||
<tt class="docutils literal">principals</tt>. <tt class="docutils literal">group:moderators</tt> par exemple.</p>
|
||
<p>Ainsi, si Bob se connecte avec <em>Firefox Accounts</em> sur le <em>bucket</em>
|
||
<tt class="docutils literal">servicedenuages_blog</tt> dans lequel il fait partie du groupe
|
||
<tt class="docutils literal">moderators</tt>, il aura la liste de <em>principals</em> suivante :
|
||
<tt class="docutils literal">Everyone, Authenticated, fxa:32aa95a474c984d41d395e2d0b614aa2, group:moderators</tt></p>
|
||
<p>Il est donc possible d'assigner une permission à Bob en utilisant l'un de
|
||
ces quatre <em>principals</em>.</p>
|
||
<div class="admonition note">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">Le <em>principal</em> <tt class="docutils literal">&lt;userid&gt;</tt> dépend du <em>back-end</em> d'authentification (e.g.
|
||
<tt class="docutils literal">github:leplatrem</tt>).</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="quelques-exemples">
|
||
<h3>Quelques exemples</h3>
|
||
<p><strong>Blog</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="35%" />
|
||
<col width="18%" />
|
||
<col width="46%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><tt class="docutils literal">bucket:blog</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;blog</span> owner id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">collection:articles</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal">group:moderators</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">record:569e28r98889</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;co-author</span> id&gt;</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Wiki</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="35%" />
|
||
<col width="18%" />
|
||
<col width="46%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Object</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><tt class="docutils literal">bucket:wiki</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;wiki</span> administrator id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">collection:articles</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Sondages</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="34%" />
|
||
<col width="31%" />
|
||
<col width="35%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td rowspan="2"><tt class="docutils literal">bucket:poll</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;admin</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">collection:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal"><span class="pre">collection:&lt;poll</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;poll</span> author id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">record:create</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Cartes colaboratives</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="34%" />
|
||
<col width="31%" />
|
||
<col width="35%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td rowspan="2"><tt class="docutils literal">bucket:maps</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;admin</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">collection:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal"><span class="pre">collection:&lt;map</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;map</span> author id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal"><span class="pre">record:&lt;record</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;maintainer</span> id&gt;</tt>
|
||
(<em>ex. event staff member
|
||
maintaining venues</em>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Plateformes</strong></p>
|
||
<p>Bien sûr, il y a plusieurs façons de modéliser les cas d'utilisation typiques.
|
||
Par exemple, on peut imaginer une plateforme de wikis (à la wikia.com), où les
|
||
wikis sont privés par défaut et certaines pages peuvent être rendues publiques :</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="32%" />
|
||
<col width="30%" />
|
||
<col width="38%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td rowspan="3"><tt class="docutils literal">bucket:freewiki</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;administrator</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">collection:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">group:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal"><span class="pre">collection:&lt;wiki</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;wiki</span> owner id&gt;</tt>,
|
||
<tt class="docutils literal"><span class="pre">group:&lt;editors</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">group:&lt;readers</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal"><span class="pre">record:&lt;page</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="l-api-http">
|
||
<h3>L'API HTTP</h3>
|
||
<p>Lors de la création d'un objet, l'utilisateur se voit
|
||
attribué la permission <tt class="docutils literal">write</tt> sur l'objet :</p>
|
||
<div class="highlight"><pre><span></span><span class="nf">PUT</span> <span class="nn">/v1/buckets/servicedenuages_blog</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||
<span class="na">Authorization</span><span class="o">:</span> <span class="l">Bearer 0b9c2625dc21ef05f6ad4ddf47c5f203837aa32ca42fced54c2625dc21efac32</span>
|
||
<span class="na">Accept</span><span class="o">:</span> <span class="l">application/json</span>
|
||
|
||
HTTP/1.1 201 Created
|
||
Content-Type: application/json; charset=utf-8
|
||
|
||
{
|
||
&quot;id&quot;: &quot;servicedenuages_blog&quot;,
|
||
&quot;permissions&quot;: {
|
||
&quot;write&quot;: [&quot;fxa:49d02d55ad10973b7b9d0dc9eba7fdf0&quot;]
|
||
}
|
||
}
|
||
</pre></div>
|
||
<p>Il est possible d'ajouter des permissions à l'aide de <tt class="docutils literal">PATCH</tt> :</p>
|
||
<div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/v1/buckets/servicedenuages_blog/collections/articles</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||
<span class="na">Authorization</span><span class="o">:</span> <span class="l">Bearer 0b9c2625dc21ef05f6ad4ddf47c5f203837aa32ca42fced54c2625dc21efac32</span>
|
||
<span class="na">Accept</span><span class="o">:</span> <span class="l">application/json</span>
|
||
|
||
{
|
||
&quot;permissions&quot;: {
|
||
&quot;read&quot;: [&quot;+system.Everyone&quot;]
|
||
}
|
||
}
|
||
|
||
HTTP/1.1 201 Created
|
||
Content-Type: application/json; charset=utf-8
|
||
|
||
{
|
||
&quot;id&quot;: &quot;servicedenuages_blog&quot;,
|
||
&quot;permissions&quot;: {
|
||
&quot;write&quot;: [&quot;fxa:49d02d55ad10973b7b9d0dc9eba7fdf0&quot;],
|
||
&quot;read&quot;: [&quot;system.Everyone&quot;]
|
||
}
|
||
}
|
||
</pre></div>
|
||
<p>Pour le <tt class="docutils literal">PATCH</tt> nous utilisons la syntaxe préfixée par un <tt class="docutils literal">+</tt> ou
|
||
par un <tt class="docutils literal">-</tt> pour ajouter ou enlever des <em>principals</em> sur un ACL.</p>
|
||
<p>Il est également possible de faire un <tt class="docutils literal">PUT</tt> pour réinitialiser les ACLs,
|
||
sachant que le <tt class="docutils literal">PUT</tt> va ajouter l'utilisateur courant à la
|
||
liste automatiquement mais qu'il pourra se retirer avec un <tt class="docutils literal">PATCH</tt>.
|
||
Ajouter l'utilisateur courant permet d'éviter les situations où plus
|
||
personne n'a accès aux données.</p>
|
||
<div class="admonition note">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">La permission <tt class="docutils literal">create</tt> est valable pour <tt class="docutils literal">POST</tt> mais aussi pour <tt class="docutils literal">PUT</tt>
|
||
lorsque l'enregistrement n'existe pas.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="le-cas-specifique-des-donnees-utilisateurs">
|
||
<h3>Le cas spécifique des données utilisateurs</h3>
|
||
<p>Une des fonctionnalités actuelles de <em>Kinto</em> est de pouvoir gérer des
|
||
collections d'enregistrements par utilisateur.</p>
|
||
<p>Sous <em>*nix</em> il est possible, pour une
|
||
application, de sauvegarder la configuration de l'utilisateur courant
|
||
dans son dossier personnel sans se soucier de l'emplacement sur
|
||
le disque en utilisant <tt class="docutils literal">~/</tt>.</p>
|
||
<p>Dans notre cas si une application souhaite sauvegarder les contacts d'un
|
||
utilisateur, elle peut utiliser le raccourci <tt class="docutils literal">~</tt> pour faire référence au
|
||
<em>bucket</em> <strong>personnel</strong> de l'utilisateur : <tt class="docutils literal"><span class="pre">/buckets/~/collections/contacts</span></tt></p>
|
||
<p>Cette URL retournera le code <tt class="docutils literal">HTTP 307</tt> vers le <em>bucket</em> de l'utilisateur courant :</p>
|
||
<div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/v1/buckets/~/collections/contacts/records</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||
|
||
{
|
||
&quot;name&quot;: &quot;Rémy&quot;,
|
||
&quot;emails&quot;: [&quot;remy@example.com&quot;],
|
||
&quot;phones&quot;: [&quot;+330820800800&quot;]
|
||
}
|
||
|
||
HTTP/1.1 307 Temporary Redirect
|
||
Location: /v1/buckets/fxa:49d02d55ad10973b7b9d0dc9eba7fdf0/collections/contacts/records
|
||
</pre></div>
|
||
<p>Ainsi il est tout à fait possible à Alice de partager ses contacts
|
||
avec Bob. Il lui suffit pour cela de donner la permission <tt class="docutils literal">read</tt> à
|
||
Bob sur sa collection et de donner l'URL complète
|
||
<tt class="docutils literal">/v1/buckets/fxa:49d02d55ad10973b7b9d0dc9eba7fdf0/collections/contacts/records</tt>
|
||
à Bob.</p>
|
||
</div>
|
||
<div class="section" id="la-delegation-des-permissions">
|
||
<h3>La délégation des permissions</h3>
|
||
<p>Dans le cas de <em>Kinto</em>, nous avons défini un format pour restreindre les
|
||
permissions via les scopes OAuth2:
|
||
<tt class="docutils literal"><span class="pre">storage:&lt;bucket_id&gt;:&lt;collection_id&gt;:&lt;permissions_list&gt;</span></tt>.</p>
|
||
<p>Ainsi, si on reprend l'exemple précédent de la liste de tâches, il est possible pour
|
||
Bob de créer un token OAuth spécifique avec les <em>scopes</em> suivants :
|
||
<tt class="docutils literal">profile storage:todolist:tasks:write <span class="pre">storage:~:contacts:read+records:create</span></tt></p>
|
||
<p>Donc, bien que Bob a la permission <tt class="docutils literal">write</tt> sur ses contacts,
|
||
l'application utilisant ce token pourra uniquement lire les contacts
|
||
existants et en ajouter de nouveaux.</p>
|
||
<p>Une partie de la complexité est donc de réussir à présenter ces <em>scopes</em> de
|
||
manière lisible à l'utilisateur, afin qu'il choisisse quelles permissions
|
||
donner aux applications qui agissent en son nom.</p>
|
||
<p>Voilà où nous en sommes de notre réflexion !</p>
|
||
<p>Si vous avez des choses à ajouter, des points de désaccord ou autres
|
||
réflexions, n'hésitez pas à nous interrompre pendant qu'il est encore temps !</p>
|
||
</div>
|
||
</div>
|
||
</content><category term="Technologie"></category></entry><entry><title>Service de nuages !</title><link href="https://blog.notmyidea.org/service-de-nuages-fr.html" rel="alternate"></link><published>2015-04-01T00:00:00+02:00</published><updated>2015-04-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-04-01:/service-de-nuages-fr.html</id><summary type="html"><p class="first last">Retour sur le premier trimestre 2015: Readinglist, Kinto, Cliquet.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à Mozilla</em></p>
|
||
<p>Pas mal de changements depuis le début de l'année pour l'équipe
|
||
«cloud-services» francophone!</p>
|
||
<p>Tout d'abord, nouvelle importante, l'équipe s'étoffe avec des profils assez
|
||
complémentaires: <a class="reference external" href="https://nicolas.perriault.net/">n1k0</a> et <a class="reference external" href="http://mathieu-leplatre.info">Mathieu</a> sont venus prêter main forte à <a class="reference external" href="http://ziade.org/">Tarek</a>, <a class="reference external" href="http://natim.ionyse.com">Rémy</a> et <a class="reference external" href="http://notmyidea.org">Alexis</a>.</p>
|
||
<p>Le début de l'année a vu le lancement de <a class="reference external" href="https://www.mozilla.org/en-US/firefox/hello/">Firefox Hello</a> ce qui nous a permis de passer
|
||
à l'échelle <a class="reference external" href="https://github.com/mozilla-services/loop-server">le serveur</a>,
|
||
écrit en Node.js®, pour l'occasion.</p>
|
||
<div class="section" id="un-serveur-de-listes-de-lecture">
|
||
<h2>Un serveur de listes de lecture</h2>
|
||
<p>En parallèle, un projet de <a class="reference external" href="https://readinglist.readthedocs.org">synchronisation de liste de lecture</a> (<em>Reading List</em>) a vu le jour. L'idée
|
||
étant de pouvoir marquer des pages &quot;à lire pour plus tard&quot; et de continuer la
|
||
lecture sur n'importe quel périphérique synchronisé (Firefox pour Android ou
|
||
Firefox Desktop). Un équivalent libre à <a class="reference external" href="http://getpocket.com">Pocket</a> en quelque sorte, qu'il est
|
||
possible d'héberger soit-même.</p>
|
||
<img alt="Capture d'écran de Firefox nightly avec readinglist." src="https://blog.notmyidea.org/images/readinglist-screenshot.png" />
|
||
<p>Pour le construire, nous aurions pu réutiliser <a class="reference external" href="https://github.com/mozilla-services/server-syncstorage">Firefox Sync</a>, après tout
|
||
c'est un service de synchronisation de données très robuste, construit avec <a class="reference external" href="http://cornice.readthedocs.org/">Cornice</a>.
|
||
Mais seulement, <em>Sync</em> n'a pas été pensé pour garantir la pérennité des données,
|
||
et la marche était trop haute pour changer ça en profondeur.</p>
|
||
<p>Nous aurions pu aussi nous contenter de faire une énième application qui expose
|
||
une API et persiste des données dans une base de données.</p>
|
||
<p>Mais cette nouvelle petite équipe n'est pas là par hasard :)</p>
|
||
</div>
|
||
<div class="section" id="la-daybed-team">
|
||
<h2>La «Daybed Team»</h2>
|
||
<p>On partage une vision: un service générique de stockage de données ! Peut-être
|
||
que ça vous rappelle <a class="reference external" href="https://daybed.io">un certain projet nommé Daybed</a> ?
|
||
Pour les applications clientes, JavaScript, mobiles ou autres, l'utilisation de
|
||
ce service doit être un jeu d'enfant ! L'application gère ses données
|
||
localement (aka offline-first), et synchronise à la demande.</p>
|
||
<p>Ici, le cœur du serveur <em>Reading List</em> est justement une API &quot;CRUD&quot; (Create,
|
||
Retrieve, Update, Delete), qui gère de la synchronisation et de
|
||
l'authentification. Nous avons donc pris le parti de faire une API &quot;simple&quot;,
|
||
avec le moins de spécificités possible, qui poserait les bases d'un service
|
||
générique. Notamment parce qu'il y a d'autres projets dans la même trempe qui vont suivre.</p>
|
||
<p>Pas mal d'expérience ayant été accumulée au sein de l'équipe, avec d'une part la
|
||
création de <em>Firefox Sync</em>, et d'autre part avec <em>Daybed</em>, notre side-project, nous
|
||
tentons de ne pas reproduire les mêmes erreurs, tout en gardant les concepts
|
||
qui ont fait leurs preuves.</p>
|
||
<p>Par exemple, nous avons conservé le mécanisme de collections d'enregistrements
|
||
et de <em>timestamp</em> de <em>Sync</em>. Comme ces problématiques sont récurrentes, voire
|
||
incontournables, nous avons décidé de reprendre le protocole de synchronisation,
|
||
de l'étendre légèrement et surtout de le dissocier du projet de listes de lecture.</p>
|
||
</div>
|
||
<div class="section" id="le-mecanisme-qui-force-a-aller-de-l-avant">
|
||
<h2>Le mécanisme qui force à aller de l'avant</h2>
|
||
<p>Comme première pierre à l'édifice, nous avons donné naissance au projet
|
||
<a class="reference external" href="https://cliquet.readthedocs.org">Cliquet</a>, dont l'idée principale est de
|
||
fournir une implémentation de ce protocole en python, tout en factorisant
|
||
l'ensemble de nos bonnes pratiques (pour la prod notamment).</p>
|
||
<img alt="Logo du projet Cliquet" class="align-right" src="https://blog.notmyidea.org/images/cliquet/cliquet-logo.png" />
|
||
<p>L'avantage d'avoir un protocole plutôt qu'un monolithe, c'est que si vous
|
||
préférez Asyncio, io.js ou Go, on vous encouragera à publier votre
|
||
implémentation alternative !</p>
|
||
<p>Avec <em>Cliquet</em>, le code du serveur liste de lecture consiste principalement
|
||
à définir un schéma pour les enregistrements, puis à forcer des valeurs de
|
||
champs sur certains appels. Cela réduit ce projet à quelques dizaines de lignes
|
||
de code.</p>
|
||
<p>Quant au futur service de stockage générique, <a class="reference external" href="http://kinto.readthedocs.org">le projet</a> en est encore à ses balbutiements mais c'est
|
||
bel et bien en route ! Il permet déjà d'être branché comme backend de stockage
|
||
dans une application <em>Cliquet</em>, et ça <a class="reference external" href="https://github.com/mozilla-services/kinto/blob/0.2.1/kinto/views/collection.py">implémenté en 20 lignes de code</a>!</p>
|
||
<p>Ah, et cette fois, nous ne construirons les fonctionnalités qu'à partir des
|
||
besoins concrets qui surviennent. Ça paraît tout bête, mais sur <em>Daybed</em> on
|
||
l'a pas vu venir :)</p>
|
||
<p>Dans les prochains articles, nous avons prévu de décrire les bonnes pratiques
|
||
rassemblées dans le protocole (ou <em>Cliquet</em>), certains points techniques précis
|
||
et de vous présenter notre vision via des exemples et tutoriaux.</p>
|
||
<p>À bientôt, donc !</p>
|
||
</div>
|
||
</content><category term="Technologie"></category></entry></feed> |