mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 19:42:37 +02:00
1284 lines
No EOL
119 KiB
XML
1284 lines
No EOL
119 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&#8217;intégrité des données en utilisant les&nbsp;signatures.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;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&nbsp;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
|
||
(<span class="caps">CRL</span>) de certificats <span class="caps">SSL</span></a>.</p>
|
||
<p>Il est évidemment nécessaire de s&#8217;assurer que les données qui sont téléchargées
|
||
sur les client sont légitimes : que personne ne tente d&#8217;invalider des
|
||
certificats alors qu&#8217;ils sont valides, et que l&#8217;ensemble des mises à jour sont
|
||
bel et bien récupérées sur le&nbsp;client.</p>
|
||
<p>La signature garantit qu&#8217;une mise à jour contient tous les enregistrements, mais il
|
||
est toujours possible de bloquer l&#8217;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&#8217;autres parties de Firefox, et vous pouvez également en tirer
|
||
parti pour d&#8217;autres cas&nbsp;d&#8217;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&#8217;on peut
|
||
facilement <em>cacher</em> les collections derrière un <span class="caps">CDN</span>.</p>
|
||
<p>Par contre, nous ne souhaitons pas que les clients fassent
|
||
confiance aveuglément, ni au serveur Kinto, ni au <span class="caps">CDN</span>.</p>
|
||
<p>Effectivement, un attaquant, contrôlant l&#8217;un ou l&#8217;autre, pourrait
|
||
alors envoyer les mises à jour qu&#8217;il souhaite à l&#8217;ensemble des clients
|
||
ou supprimer des certificats révoqués. Imaginez le carnage&nbsp;!</p>
|
||
<p>Afin de résoudre ce problème, considérons les conditions&nbsp;suivantes:</p>
|
||
<ul class="simple">
|
||
<li>La personne qui a le pouvoir de mettre à jour les <span class="caps">CRL</span> (<em>l&#8217;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 <span class="caps">HSM</span></a>) qui lui permet de
|
||
signer la&nbsp;collection;</li>
|
||
<li>Le pendant public de ce certificat est stocké et distribué dans&nbsp;Firefox;</li>
|
||
<li>Le <em>hashing</em> et la <em>signature</em> sont faits côté client pour éviter certains
|
||
vecteurs d&#8217;attaque (si un attaquant a la main sur le serveur Kinto par&nbsp;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&nbsp;entrée.</p>
|
||
<div class="section" id="premier-envoi-de-donnees-sur-kinto">
|
||
<h2>Premier envoi de données sur&nbsp;Kinto</h2>
|
||
<p>L&#8217;ensemble des données est récupéré depuis une source <em>sécurisée</em> puis mis dans
|
||
une collection <span class="caps">JSON</span>. Chaque élément contient un identifiant unique généré sur
|
||
le&nbsp;client.</p>
|
||
<p>Par exemple, un enregistrement peut ressembler à&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s2">&quot;fingerprint&quot;</span><span class="o">:</span><span class="w"> </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&nbsp;détails).</p>
|
||
<p>La signature est déportée sur un service qui ne s&#8217;occupe que de ça, puisque la
|
||
sécurité du certificat qui s&#8217;occupe des signatures est extrêmement&nbsp;importante.</p>
|
||
</div>
|
||
<div class="section" id="comment-verifier-l-integrite-des-donnees">
|
||
<h2>Comment vérifier l&#8217;intégrité des données&nbsp;?</h2>
|
||
<p>Premièrement, il faut récupérer l&#8217;ensemble des enregistrements présents sur
|
||
le serveur, ainsi que le <em>hash</em> et la signature&nbsp;associée.</p>
|
||
<p>Ensuite, vérifier la signature du <em>hash</em>, pour s&#8217;assurer que celui-ci provient
|
||
bien d&#8217;un tiers de&nbsp;confiance.</p>
|
||
<p>Finalement, recalculer le <em>hash</em> localement et valider qu&#8217;il correspond bien à
|
||
celui qui a été&nbsp;signé.</p>
|
||
</div>
|
||
<div class="section" id="ajouter-de-nouvelles-donnees">
|
||
<h2>Ajouter de nouvelles&nbsp;données</h2>
|
||
<p>Pour l&#8217;ajout de nouvelles données, il est nécessaire de s&#8217;assurer que les
|
||
données que l&#8217;on a localement sont valides avant de faire quoi que ce soit&nbsp;d&#8217;autre.</p>
|
||
<p>Une fois ces données validées, il suffit de procéder comme la première fois, et
|
||
d&#8217;envoyer à nouveau le <em>hash</em> de la collection au&nbsp;serveur.</p>
|
||
</div>
|
||
<div class="section" id="comment-calculer-ce-hash">
|
||
<h2>Comment calculer ce hash&nbsp;?</h2>
|
||
<p>Pour calculer le <em>hash</em> de la collection, il est nécessaire&nbsp;:</p>
|
||
<ol class="arabic simple">
|
||
<li>D&#8217;ordonner l&#8217;ensemble des éléments de la collection (par leur id)&nbsp;;</li>
|
||
<li>Pour chaque élément, sérialiser les champs qui nous intéressent (les
|
||
concaténer clé +&nbsp;valeur)</li>
|
||
<li>Calculer le <em>hash</em> depuis la&nbsp;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"><span class="caps">JSON</span> Web Signature</a> semblent
|
||
une piste intéressante. En attendant, une implementation naïve en python
|
||
pourrait ressembler à ceci&nbsp;:</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="code"></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&nbsp;mois.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Mozilla a pour coutume d&#8217;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>&nbsp;!</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&#8217;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&#8217;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&nbsp;mois.</p>
|
||
<div class="section" id="ateliers-et-promotion">
|
||
<h2>Ateliers et&nbsp;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&#8217;introduction</a>.</p>
|
||
<p>L&#8217;application résultante, pourtant toute simple, permet d&#8217;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&#8217;honneur à faire du Vanilla.<span class="caps">JS</span>, déjà pour éviter les
|
||
combats de clochers autour des frameworks, mais aussi pour mettre en évidence qu&#8217;avec
|
||
<span class="caps">HTML5</span> et <span class="caps">ES6</span>, on n&#8217;était plus aussi démunis qu&#8217;il y a quelques&nbsp;années.</p>
|
||
<p>Ce petit atelier nous a permis de nous rendre compte qu&#8217;on avait encore de
|
||
grosses lacunes en terme de documentation, surtout en ce qui concerne
|
||
l&#8217;éco-système et la vision globale des projets (Kinto, Kinto.js, Cliquet, &#8230;).
|
||
Nous allons donc faire de notre mieux pour combler ce&nbsp;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&nbsp;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&nbsp;abonnements.</p>
|
||
<p>Pour ce projet, Kinto sera utilisé depuis une application Django, via un client&nbsp;Python.</p>
|
||
<p>Maintenant que les développements ont été livrés, il faut transformer l&#8217;essai, réussir l&#8217;intégration, l&#8217;hébergement et la montée en puissance. La solution doit être livrée à la fin de&nbsp;l&#8217;année.</p>
|
||
<div class="section" id="a-venir">
|
||
<h3>À&nbsp;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&nbsp;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 <span class="caps">OS</span> et&nbsp;stockage</h2>
|
||
<p>Nous avons eu beaucoup d&#8217;échanges avec l&#8217;équipe de Firefox <span class="caps">OS</span>, avec qui nous avions
|
||
déjà eu l&#8217;occasion de collaborer, pour le <a class="reference external" href="https://github.com/mozilla-services/msisdn-gateway">serveur d&#8217;identification BrowserID par <span class="caps">SMS</span></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&nbsp;sync</h3>
|
||
<p>Kinto, la solution simple promue pour la synchronisation de données dans les applications
|
||
Firefox <span class="caps">OS</span> ? La classe ! C&#8217;est ce qu&#8217;on avait en tête depuis longtemps, déjà à
|
||
l&#8217;époque avec <a class="reference external" href="http://daybed.readthedocs.org/">Daybed</a>. Voici donc une belle opportunité à saisir&nbsp;!</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&nbsp;:)</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é&nbsp;!</p>
|
||
</div>
|
||
<div class="section" id="cut-the-rope">
|
||
<h3>Cut the&nbsp;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&nbsp;!</p>
|
||
</div>
|
||
<div class="section" id="syncto">
|
||
<h3>« SyncTo&nbsp;»</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, &#8230;) entre plusieurs périphériques, de manière&nbsp;chiffrée.</p>
|
||
<p>L&#8217;implémentation du client en JavaScript est relativement complexe et date un peu maintenant.
|
||
Le code existant n&#8217;est pas vraiment portable dans <em>Firefox <span class="caps">OS</span></em> et les tentatives de réécriture
|
||
n&#8217;ont pas&nbsp;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&#8217;agira surtout de
|
||
câbler l&#8217;authentification BrowserId et la&nbsp;Crypto.</p>
|
||
<p>Alexis a sauté sur l&#8217;occasion pour commencer l&#8217;écriture d&#8217;<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&#8217;écriture du&nbsp;service.</p>
|
||
</div>
|
||
<div class="section" id="cloud-storage">
|
||
<h3>Cloud&nbsp;Storage</h3>
|
||
<p>Eden Chuang et Sean Lee ont présenté les avancées sur l&#8217;intégration de services de stockages
|
||
distants (<em>DropBox, Baidu Yun</em>) dans <em>Firefox <span class="caps">OS</span></em>. Actuellement, leur preuve de
|
||
concept repose sur <a class="reference external" href="https://fr.wikipedia.org/wiki/Filesystem_in_Userspace"><span class="caps">FUSE</span></a>.</p>
|
||
<p>Nous avons évidemment en tête d&#8217;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&#8217;instant les cas d&#8217;utilisations ne se sont pas encore présentés&nbsp;officiellement.</p>
|
||
</div>
|
||
<div class="section" id="a-venir-1">
|
||
<h3>À&nbsp;venir</h3>
|
||
<p>Nous serons probablement amenés à introduire la gestion de la concurrence dans
|
||
le client <span class="caps">JS</span>, en complément de ce qui a été fait sur le serveur, pour permettre
|
||
les écritures simultanées et synchronisation en tâche de&nbsp;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>&nbsp;!</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&nbsp;Firefox</h2>
|
||
<p>Aujourd&#8217;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, &#8230;</em>) <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a>.</p>
|
||
<p>Il s&#8217;agit de données <span class="caps">JSON</span> 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>, &#8230;),
|
||
et pour l&#8217;instant, aucun choix n&#8217;a été fait. Mais lors des conversations avec
|
||
l&#8217;é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&nbsp;!</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>La bonne nouvelle c&#8217;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&nbsp;bar</h2>
|
||
<p>L&#8217;é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 <span class="caps">URL</span>, historique et&nbsp;recherche.</p>
|
||
<p>Nous ne pouvons pas en dire beaucoup plus pour l&#8217;instant, mais les fonctionnalités
|
||
de collections d&#8217;enregistrements partagées entre utilisateurs de <em>Kinto</em>
|
||
correspondent parfaitement à ce qui est envisagé pour le futur du navigateur&nbsp;:)</p>
|
||
<div class="section" id="a-venir-2">
|
||
<h3>À&nbsp;venir</h3>
|
||
<p>Nous serons donc probablement amenés, avant de la fin de l&#8217;année, à introduire des
|
||
fonctionnalités d&#8217;indexation et de recherche <em>full-text</em> (comprendre <em>ElasticSearch</em>).
|
||
Cela rejoint nos plans précédents, puisque c&#8217;est quelque chose que nous avions dans
|
||
<em>Daybed</em>, et qui figurait sur notre feuille de route&nbsp;!</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&#8217;équipe <em>Recherche</em> explore les notions de plateforme, et travaille notamment
|
||
sur l&#8217;implémentation d&#8217;un navigateur en <span class="caps">JS</span>/<span class="caps">HTML</span> 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&#8217;équipe pour synchroniser les données associées à un&nbsp;utilisateur.</p>
|
||
<p>Il pourrait s&#8217;agir de données de navigation (comme Sync), mais aussi de collections
|
||
d&#8217;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&#8217;<span class="caps">URL</span> sans
|
||
interroger le moteur de&nbsp;recherche.</p>
|
||
<p>L&#8217;exercice pourrait être poussé jusqu&#8217;à la synchronisation d&#8217;états <em>React</em>
|
||
entre périphériques (par exemple pour les&nbsp;onglets).</p>
|
||
<div class="section" id="a-venir-3">
|
||
<h3>À&nbsp;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 <span class="caps">JS</span>. Ça tombe bien, c&#8217;est un
|
||
sujet passionant, et <a class="reference external" href="http://www.w3.org/TR/WebCryptoAPI/">il y a plusieurs standards</a>&nbsp;!</p>
|
||
<p>Pour éviter d&#8217;interroger le serveur à intervalle régulier afin de synchroniser les
|
||
changements, l&#8217;introduction des <a class="reference external" href="https://w3c.github.io/push-api/">*push notifications*</a> semble assez naturelle.
|
||
Il s&#8217;agirait alors de la dernière pierre qui manque à l&#8217;édifice pour obtenir
|
||
un «<em>Mobile/Web backend as a service</em>»&nbsp;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&nbsp;équipes.</p>
|
||
<p>L&#8217;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&nbsp;:)</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="code"></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&nbsp;!</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Aujourd&#8217;hui, c&#8217;est jour de fête : nous venons de publier Cliquet <strong>2.0</strong>
|
||
<a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a> et Kinto <strong>1.0</strong> <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[2]</a>.</p>
|
||
<blockquote class="epigraph">
|
||
<p>L&#8217;aboutissement de 3 années de R&amp;D&nbsp;!</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&#8217;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&#8217;implémentation de
|
||
micro-services <span class="caps">HTTP</span> tels que les APIs <em><span class="caps">REST</span></em> ayant des besoins de&nbsp;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&#8217;on attendait depuis longtemps, comme la gestion des&nbsp;permissions.</p>
|
||
<p>Bien sur, exactement comme après un ré-aménagement de salon, on ne peut
|
||
s&#8217;empêcher de voir toutes les choses qui doivent toujours être améliorées,
|
||
notamment sur la documentation et les&nbsp;performances.</p>
|
||
<p>On peut déjà entrevoir à quoi l&#8217;écosystème va ressembler, et c&#8217;est prometteur.
|
||
Il y a déjà un client JavaScript <a class="footnote-reference" href="#footnote-3" id="footnote-reference-3">[3]</a> dont l&#8217;objectif est de synchroniser les
|
||
données locales du navigateur avec une instance de&nbsp;Kinto.</p>
|
||
<p>N&#8217;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 <span class="caps">IRC</span> de&nbsp;Mozilla.</p>
|
||
<p>Et ce n&#8217;est que le début ! Le futur se dessine dans notre feuille de route
|
||
<a class="footnote-reference" href="#footnote-4" id="footnote-reference-4">[4]</a>.</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td><strong>Cliquet</strong> est une boite à outils pour faciliter l&#8217;implémentation de
|
||
microservices <span class="caps">HTTP</span> tels que les APIs <em><span class="caps">REST</span></em> ayant des besoins de
|
||
synchronisation.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[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&#8217;authentification
|
||
est <em>pluggable</em>).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="footnote-3" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-3">[3]</a></td><td>Cliquetis, la bibliothèque JavaScript pour consommer l&#8217;<span class="caps">API</span> <span class="caps">HTTP</span> 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="footnote-4" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-4">[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="code"></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&nbsp;?</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;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&nbsp;?</strong></p>
|
||
<div class="section" id="la-problematique">
|
||
<h2>La&nbsp;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&#8217;accès à un objet pour la personne qui fait la&nbsp;requête.</p>
|
||
<p>Chaque requête sur notre <span class="caps">API</span> va générer une ou plusieurs demandes
|
||
d&#8217;accès, il faut donc que la réponse soit très rapide sous peine
|
||
d&#8217;impacter la vélocité du&nbsp;service.</p>
|
||
</div>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-utilisateur">
|
||
<h2>Obtenir la liste des &#8220;principals&#8221; d&#8217;un&nbsp;utilisateur</h2>
|
||
<p>Les <em>principals</em> de l&#8217;utilisateur correspondent à son <tt class="docutils literal">user_id</tt>
|
||
ainsi qu&#8217;à la liste des identifiants des groupes dans lesquels il a
|
||
été&nbsp;ajouté.</p>
|
||
<p>Pour éviter de recalculer les <em>principals</em> de l&#8217;utilisateur à chaque
|
||
requête, le mieux reste de maintenir une liste des <em>principals</em> par&nbsp;utilisateur.</p>
|
||
<p>Ainsi lorsqu&#8217;on ajoute un utilisateur à un groupe, il faut bien penser
|
||
à ajouter le groupe à la liste des <em>principals</em> de&nbsp;l&#8217;utilisateur.</p>
|
||
<p>Ça se complexifie lorsqu&#8217;on ajoute un groupe à un&nbsp;groupe.</p>
|
||
<p>Dans un premier temps interdire l&#8217;ajout d&#8217;un groupe à un groupe est
|
||
une limitation qu&#8217;on est prêts à accepter pour simplifier le&nbsp;modèle.</p>
|
||
<p>L&#8217;avantage de maintenir la liste des <em>principals</em> d&#8217;un utilisateur
|
||
lors de la modification de cette liste c&#8217;est qu&#8217;elle est déjà
|
||
construite lors des lectures, qui sont dans notre cas plus fréquentes
|
||
que les&nbsp;é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 <span class="caps">URI</span>:
|
||
<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 &#8220;principals&#8221; d&#8217;un <span class="caps">ACE</span></h2>
|
||
<blockquote>
|
||
Rappel, un &#8220;<span class="caps">ACE</span>&#8221; est un <em>Access Control Entry</em>, un des éléments
|
||
d&#8217;une <span class="caps">ACL</span> (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&#8217;un
|
||
objet héritent de celle de l&#8217;objet&nbsp;parent.</p>
|
||
<p>Par exemple, avoir le droit d&#8217;écriture sur un <em>bucket</em> permet la
|
||
création des permissions et la modification de tous ses&nbsp;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&nbsp;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&#8217;héritage de chaque permission</a>.</p>
|
||
<p>Prenons l&#8217;exemple de l&#8217;ajout d&#8217;un record dans une&nbsp;collection.</p>
|
||
<p>Le droit <tt class="docutils literal">records:create</tt> est obtenu si l&#8217;on a l&#8217;un des droits&nbsp;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&#8217;une
|
||
modification d&#8217;<span class="caps">ACL</span>. Cependant cela nécessitait de construire cette
|
||
liste lors de l&#8217;ajout d&#8217;un objet et de mettre à jour tout l&#8217;arbre lors
|
||
de sa suppression. (<em>Je vous laisse imaginer le nombre d&#8217;opérations
|
||
nécessaires pour ajouter un administrateur sur un *bucket</em> contenant
|
||
1000 collections avec 100000 records&nbsp;chacune.*)</p>
|
||
<p>La solution que nous avons désormais adoptée consiste à stocker les
|
||
<em>principals</em> de chaque <em><span class="caps">ACE</span></em> (<em>qui</em> a le droit de faire telle action
|
||
sur l&#8217;objet), et de faire l&#8217;union des <em><span class="caps">ACE</span></em> hérités, afin de les
|
||
croiser avec les <em>principals</em> de l&#8217;utilisateur&nbsp;:</p>
|
||
<blockquote>
|
||
(<span class="caps">ACE</span>(object, permission) ∪ inherited_ACE) ∩ <span class="caps">PRINCIPALS</span>(user)</blockquote>
|
||
<p>Par exemple l&#8217;<span class="caps">ACE</span>: <tt class="docutils literal">/buckets/blog/collections/article:records:create</tt> hérite de
|
||
l&#8217;<span class="caps">ACE</span> <tt class="docutils literal">/buckets/blog/collections/article:write</tt> et de <tt class="docutils literal">/buckets/blog:write</tt> :</p>
|
||
<blockquote>
|
||
(<span class="caps">ACE</span>(/buckets/blog/collections/article:records:create) ∪ <span class="caps">ACE</span>(/buckets/blog/collections/article:write) ∪ <span class="caps">ACE</span>(/buckets/blog:write)) ∩ <span class="caps">PRINCIPALS</span>(&#8216;fxa:alexis&#8217;)</blockquote>
|
||
</div>
|
||
<div class="section" id="recuperer-les-donnees-de-l-utilisateur">
|
||
<h2>Récupérer les données de&nbsp;l&#8217;utilisateur</h2>
|
||
<p>La situation se corse lorsqu&#8217;on souhaite limiter la liste des
|
||
<em>records</em> d&#8217;une collection à ceux accessibles pour l&#8217;utilisateur, car
|
||
on doit faire cette intersection pour tous les <em>records</em>.</p>
|
||
<p>Une première solution est de regarder si l&#8217;utilisateur est mentionné
|
||
dans les <em><span class="caps">ACL</span>*s du *bucket</em> ou de la <em>collection</em>:</p>
|
||
<p>Ensuite, si ce n&#8217;est pas le cas, alors on filtre les <em>records</em> pour
|
||
lesquels les <em>principals</em> correspondent à ceux de&nbsp;l&#8217;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&#8217;un utilisateur souhaitera supprimer des
|
||
enregistrements sur lesquels il a les droits de lecture mais pas&nbsp;d&#8217;écriture.</p>
|
||
</div>
|
||
<div class="section" id="le-modele-de-donnees">
|
||
<h2>Le modèle de&nbsp;données</h2>
|
||
<p>Pour avoir une idée des requêtes dans un backend <span class="caps">SQL</span>, voyons un peu ce
|
||
que donnerait le modèle de&nbsp;données.</p>
|
||
<div class="section" id="le-format-des-id">
|
||
<h3>Le format des <span class="caps">ID</span></h3>
|
||
<p>Utiliser des <span class="caps">URI</span> comme identifiant des objets présente de nombreux
|
||
avantages (lisibilité, unicité, cohérence avec les&nbsp;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&nbsp;tables</h3>
|
||
<p>Pour le stockage des principals et des&nbsp;permissions:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">user</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">[]);</span>
|
||
<span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">perms</span><span class="p">(</span><span class="n">ace</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="w"> </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><span class="caps">ACE</span></em>&nbsp;(e.g.&#8220;/buckets/blog:write&#8220;).</p>
|
||
<p>Pour le stockage des&nbsp;données:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">object</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="w"> </span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">write_principals</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">[],</span><span class="w"> </span><span class="n">read_principals</span><span class="w"> </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&#8217;objet
|
||
(e.g. groupe d&#8217;un <em>bucket</em>, collection d&#8217;un <em>bucket</em>, <em>record</em> d&#8217;une
|
||
collection,&nbsp;&#8230;).</p>
|
||
</div>
|
||
<div class="section" id="exemple-d-utilisateur">
|
||
<h3>Exemple&nbsp;d&#8217;utilisateur</h3>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;fxa:alexis&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
|
||
<span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;fxa:natim&#39;</span><span class="p">,</span>
|
||
<span class="w"> </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&nbsp;d&#8217;objets</h3>
|
||
<div class="section" id="bucket">
|
||
<h4>Bucket</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;bucket&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
|
||
<span class="w"> </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="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">,</span><span class="w"> </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="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/groups/moderators&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;group&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="w"> </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="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">,</span><span class="w"> </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&#8217;il a la permission
|
||
<tt class="docutils literal">write</tt> dans le <em>bucket</em>&nbsp;parent.</p>
|
||
</div>
|
||
<div class="section" id="collection">
|
||
<h4>Collection</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/articles&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;collection&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="w"> </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="w"> </span><span class="s1">&#39;{&quot;system.Everyone&quot;}&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;/buckets/blog/groups/moderators&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
<p>Cette collection d&#8217;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="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;record&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/articles&#39;</span><span class="p">,</span>
|
||
<span class="w"> </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="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">,</span><span class="w"> </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&nbsp;permissions</h3>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-ace-1">
|
||
<h4>Obtenir la liste des &#8220;principals&#8221; d&#8217;un <span class="caps">ACE</span></h4>
|
||
<p>Comme vu plus haut, pour vérifier une permission, on fait l&#8217;union des
|
||
<em>principals</em> requis par les objets hérités, et on teste leur
|
||
intersection avec ceux de&nbsp;l&#8217;utilisateur:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">WITH</span><span class="w"> </span><span class="n">required_principals</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">unnest</span><span class="p">(</span><span class="n">principals</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">p</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">perms</span>
|
||
<span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">ace</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog:write&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog:read&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/article:write&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/article:read&#39;</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">user_principals</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">unnest</span><span class="p">(</span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">user</span>
|
||
<span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;fxa:natim&#39;</span>
|
||
<span class="w"> </span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">user_principals</span><span class="w"> </span><span class="n">a</span>
|
||
<span class="w"> </span><span class="k">INNER</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">required_principals</span><span class="w"> </span><span class="n">b</span>
|
||
<span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">p</span><span class="w"> </span><span class="o">=</span><span class="w"> </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&nbsp;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="w"> </span><span class="k">data</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="n">o</span><span class="p">,</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="n">u</span>
|
||
<span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="k">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;record&#39;</span>
|
||
<span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="n">parent_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/article&#39;</span>
|
||
<span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">read_principals</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">u</span><span class="p">.</span><span class="n">principals</span><span class="w"> </span><span class="k">OR</span>
|
||
<span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="n">write_principals</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">u</span><span class="p">.</span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">u</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;fxa:natim&#39;</span><span class="p">;</span>
|
||
</pre></div>
|
||
<p>Les listes s&#8217;indexent bien, notamment grâce aux <a class="reference external" href="http://www.postgresql.org/docs/current/static/indexes-types.html">index <span class="caps">GIN</span></a>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="avec-redis">
|
||
<h3>Avec&nbsp;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&#8217;intersection et&nbsp;d&#8217;union.</p>
|
||
<p>Avec <em>Redis</em> on peut écrire l&#8217;obtention des <em>principals</em> pour un <em><span class="caps">ACE</span></em>
|
||
comme cela&nbsp;:</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"><span class="caps">SUNIONSTORE</span></tt> permet de créer un set contenant les éléments de
|
||
l&#8217;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&#8217;union des sets d&#8217;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"><span class="caps">SINTER</span></tt> retourne l&#8217;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&#8217;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"><span class="caps">SINTER</span></tt> n&#8217;est pas vide, alors
|
||
l&#8217;utilisateur possède la&nbsp;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"><span class="caps">MULTI</span></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&#8217;une
|
||
transaction</a>
|
||
et garantir ainsi l&#8217;intégrité de la&nbsp;requête.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="conclusion">
|
||
<h2>Conclusion</h2>
|
||
<p>La solution a l&#8217;air simple mais nous a demandé beaucoup de réflexion
|
||
en passant par plusieurs&nbsp;propositions.</p>
|
||
<p>L&#8217;idée finale est d&#8217;avoir&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>Un backend spécifique permettant de stocker les <em>principals</em> des
|
||
utilisateurs et des <em><span class="caps">ACE</span></em> (e.g. avec les sets Redis)&nbsp;;</li>
|
||
<li>La liste des principals read et write sur la table des&nbsp;objets.</li>
|
||
</ul>
|
||
<p>C&#8217;est dommage d&#8217;avoir le concept de permissions à deux endroits, mais
|
||
cela permet de connaître rapidement la permission d&#8217;un utilisateur sur
|
||
un objet et également de pouvoir récupérer tous les objets d&#8217;une
|
||
collection pour un utilisateur si celui-ci n&#8217;a pas accès à tous les
|
||
records de la collection, ou toutes les collections du&nbsp;bucket.</p>
|
||
</div>
|
||
</content><category term="code"></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&#8217;implémentation pour&nbsp;Kinto</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Dans le cadre de la création d&#8217;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&nbsp;?</p>
|
||
<p><strong>tl;dr: Quelques retours sur le vocabulaire des systèmes de permission et sur nos idées pour l&#8217;implementation des permissions dans un stockage&nbsp;générique.</strong></p>
|
||
<div class="section" id="la-problematique">
|
||
<h2>La&nbsp;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&#8217;autres&nbsp;personnes.</p>
|
||
<p>En regardant les cas d&#8217;utilisations, on se rend compte qu&#8217;on a plusieurs types
|
||
d&#8217;utilisateurs&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>les utilisateurs &#8220;finaux&#8221; (vous)&nbsp;;</li>
|
||
<li>les applications qui interagissent en leurs&nbsp;noms.</li>
|
||
</ul>
|
||
<p>Tous les intervenants n&#8217;ont donc pas les mêmes droits : certains doivent
|
||
pouvoir lire, d&#8217;autres écrire, d&#8217;autres encore créer de nouveaux
|
||
enregistrements, et le contrôle doit pouvoir s&#8217;effectuer de manière fine : il
|
||
doit être possible de lire un enregistrement mais pas un autre, par&nbsp;exemple.</p>
|
||
<p>Nous sommes partis du constat que les solutions disponibles n&#8217;apportaient pas
|
||
une réponse satisfaisante à ces&nbsp;besoins.</p>
|
||
</div>
|
||
<div class="section" id="un-probleme-de-vocabulaire">
|
||
<h2>Un problème de&nbsp;vocabulaire</h2>
|
||
<p>Le principal problème rencontré lors des réflexions fût le&nbsp;vocabulaire.</p>
|
||
<p>Voici ci-dessous une explication des différents&nbsp;termes.</p>
|
||
<div class="section" id="le-concept-de-principal">
|
||
<h3>Le concept de « principal&nbsp;»</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="#footnote-1" id="footnote-reference-1">[1]</a> En Français il s&#8217;agit
|
||
du « commettant », l&#8217;acteur qui commet l&#8217;action (oui, le terme est conceptuel&nbsp;!)</p>
|
||
<p>Il peut s&#8217;agir aussi bien d&#8217;un individu, d&#8217;un ordinateur, d&#8217;un
|
||
service ou d&#8217;un groupe regroupant l&#8217;une de ces entités, ce qui
|
||
est plus large que le classique « <em>user id</em>&nbsp;».</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&#8217;authentification dont le rôle est de
|
||
définir une liste de <em>principals</em> pour l&#8217;utilisateur se&nbsp;connectant.</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[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&nbsp;groupe</h3>
|
||
<p>De but en blanc, il n&#8217;est pas évident de définir précisément la
|
||
différence entre ces deux concepts qui permettent d&#8217;associer
|
||
des permissions à un groupe de <em>principals</em>. <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[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&nbsp;concepts.</p>
|
||
<ul class="simple">
|
||
<li>Un rôle est une liste de permissions que l&#8217;on associe à un <em>principal</em>.</li>
|
||
<li>Un groupe est une liste de <em>principals</em> que l&#8217;on peut associer à une&nbsp;permission.</li>
|
||
</ul>
|
||
<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[2]</a></td><td>Plus d&#8217;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, <span class="caps">ACL</span>, <span class="caps">ACE</span></h3>
|
||
<blockquote class="epigraph">
|
||
<p>Une <span class="caps">ACL</span> est une liste d’Access Control Entry (<span class="caps">ACE</span>) ou entrée de contrôle d&#8217;accès
|
||
donnant ou supprimant des droits d&#8217;accès à une personne ou un&nbsp;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&nbsp;exemple:</p>
|
||
<div class="highlight"><pre><span></span>create_record: alexis,remy,tarek
|
||
</pre></div>
|
||
<p>Cet <span class="caps">ACE</span> donne la permission <tt class="docutils literal">create</tt> sur l&#8217;objet <tt class="docutils literal">record</tt> aux
|
||
utilisateurs Tarek, Rémy et&nbsp;Alexis.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="la-delegation-de-permissions">
|
||
<h2>La délégation de&nbsp;permissions</h2>
|
||
<p>Imaginez l&#8217;exemple suivant, où un utilisateur stocke deux types de données en
|
||
ligne&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>des contacts&nbsp;;</li>
|
||
<li>une liste de tâches à faire qu&#8217;il peut associer à ses&nbsp;contacts.</li>
|
||
</ul>
|
||
<p>L&#8217;utilisateur a tous les droits sur ses&nbsp;données.</p>
|
||
<p>Cependant il utilise deux applications qui doivent elles avoir un accès restreint&nbsp;:</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&nbsp;nouveaux.</p>
|
||
<p>Il lui faut donc un moyen de déléguer certains de ses droits à un tiers&nbsp;(l&#8217;application).</p>
|
||
<p>C&#8217;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&#8217;un utilisateur, une fenêtre lui demande quels
|
||
accès il veut donner à l&#8217;application qui va agir en son&nbsp;nom.</p>
|
||
<p>Le service aura ensuite accès à ces <em>scopes</em> en regardant le jeton
|
||
d&#8217;authentification utilisé. Cette information doit être
|
||
considérée comme une entrée utilisateur (c&#8217;est à dire qu&#8217;on ne peut
|
||
pas lui faire confiance). Il s&#8217;agit de ce que l&#8217;utilisateur&nbsp;souhaite.</p>
|
||
<p>Or, il est également possible que l&#8217;utilisateur n&#8217;ait pas accès aux données
|
||
qu&#8217;il demande. Le service doit donc utiliser deux niveaux de permissions :
|
||
celles de l&#8217;utilisateur, et celles qui ont été déléguées à&nbsp;l&#8217;application.</p>
|
||
</div>
|
||
<div class="section" id="espace-de-noms">
|
||
<h2>Espace de&nbsp;noms</h2>
|
||
<p>Dans notre implémentation initiale de <em>Kinto</em> (notre service de stockage en
|
||
construction), l&#8217;espace de nom était implicite : les données stockées étaient
|
||
nécessairement celles de l&#8217;utilisateur&nbsp;connecté.</p>
|
||
<p>Les données d&#8217;un utilisateur étaient donc cloisonnées et impossible à&nbsp;partager.</p>
|
||
<p>L&#8217;utilisation d&#8217;espaces de noms est une manière simple de gérer le partage des&nbsp;données.</p>
|
||
<p>Nous avons choisi de reprendre le modèle de GitHub et de Bitbucket, qui
|
||
utilisent les « organisations » comme espaces de&nbsp;noms.</p>
|
||
<p>Dans notre cas, il est possible de créer des &#8220;buckets&#8221;, qui correspondent à ces
|
||
espaces de noms. Un bucket est un conteneur de collections et de groupes&nbsp;utilisateurs.</p>
|
||
<p>Les ACLs sur ces collections peuvent être attribuées à certains groupes du
|
||
<em>bucket</em> ainsi qu&#8217;à d&#8217;autres <em>principals</em>&nbsp;directement.</p>
|
||
</div>
|
||
<div class="section" id="notre-proposition-d-api">
|
||
<h2>Notre proposition d&#8217;<span class="caps">API</span></h2>
|
||
<div class="section" id="les-objets-manipules">
|
||
<h3>Les objets&nbsp;manipulés</h3>
|
||
<p>Pour mettre en place la gestion des permissions, nous avons identifié les
|
||
objets suivants&nbsp;:</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&#8217;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&#8217;enregistrements.</td>
|
||
</tr>
|
||
<tr><td><strong>record</strong></td>
|
||
<td>Un enregistrement d&#8217;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&nbsp;:</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&nbsp;permissions</h3>
|
||
<p>Pour chacun de ces objets nous avons identifié les permissions suivantes&nbsp;:</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&#8217;objet et de ses sous-objets.</td>
|
||
</tr>
|
||
<tr><td><strong>write</strong></td>
|
||
<td>La permission de modifier et
|
||
d&#8217;administrer un objet et ses sous-
|
||
objets. La permission <em>write</em> permet la
|
||
lecture, modification et suppression
|
||
d&#8217;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&#8217;objet enfant en question car un objet peut avoir plusieurs types
|
||
d&#8217;enfants. Par exemple : <tt class="docutils literal">collections:create</tt>, <tt class="docutils literal">groups:create</tt>.</p>
|
||
<p>Nous n&#8217;avons pour l&#8217;instant pas de permission pour <cite>delete</cite> et <cite>update</cite>,
|
||
puisque nous n&#8217;avons pas trouvé de cas d&#8217;utilisation qui les nécessitent.
|
||
Quiconque avec le droit d&#8217;écriture peut donc supprimer un&nbsp;enregistrement.</p>
|
||
<p>Les permissions d&#8217;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&nbsp;monde.</p>
|
||
<p>Par conséquent, les permissions sont cumulées. Autrement dit, il n&#8217;est pas
|
||
possible qu&#8217;un objet ait des permissions plus restrictives que son&nbsp;parent.</p>
|
||
<p>Voici la liste exhaustive des permissions&nbsp;:</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&#8217;est en quelque sorte le droit
|
||
d&#8217;administration du <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>C&#8217;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&#8217;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&#8217;enregistrement.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter
|
||
l&#8217;enregistrement.</td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">group</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission d&#8217;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>&nbsp;»</h3>
|
||
<p>Les acteurs se connectant au service de stockage peuvent&nbsp;s&#8217;authentifier.</p>
|
||
<p>Ils reçoivent alors une liste de <em>principals</em>&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal">Everyone</tt>: le <em>principal</em> donné à tous les acteurs (authentifiés ou pas)&nbsp;;</li>
|
||
<li><tt class="docutils literal">Authenticated</tt>: le <em>principal</em> donné à tous les acteurs authentifiés&nbsp;;</li>
|
||
<li>un <em>principal</em> identifiant l&#8217;acteur, par exemple <tt class="docutils literal">fxa:32aa95a474c984d41d395e2d0b614aa2</tt></li>
|
||
</ul>
|
||
<p>Afin d&#8217;éviter les collisions d&#8217;identifiants, le <em>principal</em> de l&#8217;acteur dépend
|
||
de son type d&#8217;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&nbsp;acteur).</p>
|
||
<p>En fonction du <em>bucket</em> sur lequel se passe l&#8217;action, les groupes dont
|
||
fait partie l&#8217;utilisateur sont également ajoutés à sa liste de
|
||
<tt class="docutils literal">principals</tt>. <tt class="docutils literal">group:moderators</tt> par&nbsp;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&#8217;assigner une permission à Bob en utilisant l&#8217;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&#8217;authentification (e.g.
|
||
<tt class="docutils literal">github:leplatrem</tt>).</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="quelques-exemples">
|
||
<h3>Quelques&nbsp;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&nbsp;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&#8217;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&nbsp;:</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&#8217;<span class="caps">API</span> <span class="caps">HTTP</span></h3>
|
||
<p>Lors de la création d&#8217;un objet, l&#8217;utilisateur se voit
|
||
attribué la permission <tt class="docutils literal">write</tt> sur l&#8217;objet&nbsp;:</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&#8217;ajouter des permissions à l&#8217;aide de <tt class="docutils literal"><span class="caps">PATCH</span></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"><span class="caps">PATCH</span></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 <span class="caps">ACL</span>.</p>
|
||
<p>Il est également possible de faire un <tt class="docutils literal"><span class="caps">PUT</span></tt> pour réinitialiser les ACLs,
|
||
sachant que le <tt class="docutils literal"><span class="caps">PUT</span></tt> va ajouter l&#8217;utilisateur courant à la
|
||
liste automatiquement mais qu&#8217;il pourra se retirer avec un <tt class="docutils literal"><span class="caps">PATCH</span></tt>.
|
||
Ajouter l&#8217;utilisateur courant permet d&#8217;éviter les situations où plus
|
||
personne n&#8217;a accès aux&nbsp;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"><span class="caps">POST</span></tt> mais aussi pour <tt class="docutils literal"><span class="caps">PUT</span></tt>
|
||
lorsque l&#8217;enregistrement n&#8217;existe&nbsp;pas.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="le-cas-specifique-des-donnees-utilisateurs">
|
||
<h3>Le cas spécifique des données&nbsp;utilisateurs</h3>
|
||
<p>Une des fonctionnalités actuelles de <em>Kinto</em> est de pouvoir gérer des
|
||
collections d&#8217;enregistrements par&nbsp;utilisateur.</p>
|
||
<p>Sous <em>*nix</em> il est possible, pour une
|
||
application, de sauvegarder la configuration de l&#8217;utilisateur courant
|
||
dans son dossier personnel sans se soucier de l&#8217;emplacement sur
|
||
le disque en utilisant <tt class="docutils literal">~/</tt>.</p>
|
||
<p>Dans notre cas si une application souhaite sauvegarder les contacts d&#8217;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&#8217;utilisateur : <tt class="docutils literal"><span class="pre">/buckets/~/collections/contacts</span></tt></p>
|
||
<p>Cette <span class="caps">URL</span> retournera le code <tt class="docutils literal"><span class="caps">HTTP</span> 307</tt> vers le <em>bucket</em> de l&#8217;utilisateur courant&nbsp;:</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&#8217;<span class="caps">URL</span> complète
|
||
<tt class="docutils literal">/v1/buckets/fxa:49d02d55ad10973b7b9d0dc9eba7fdf0/collections/contacts/records</tt>
|
||
à&nbsp;Bob.</p>
|
||
</div>
|
||
<div class="section" id="la-delegation-des-permissions">
|
||
<h3>La délégation des&nbsp;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&#8217;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&#8217;application utilisant ce token pourra uniquement lire les contacts
|
||
existants et en ajouter de&nbsp;nouveaux.</p>
|
||
<p>Une partie de la complexité est donc de réussir à présenter ces <em>scopes</em> de
|
||
manière lisible à l&#8217;utilisateur, afin qu&#8217;il choisisse quelles permissions
|
||
donner aux applications qui agissent en son&nbsp;nom.</p>
|
||
<p>Voilà où nous en sommes de notre réflexion&nbsp;!</p>
|
||
<p>Si vous avez des choses à ajouter, des points de désaccord ou autres
|
||
réflexions, n&#8217;hésitez pas à nous interrompre pendant qu&#8217;il est encore temps&nbsp;!</p>
|
||
</div>
|
||
</div>
|
||
</content><category term="code"></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,&nbsp;Cliquet.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Pas mal de changements depuis le début de l&#8217;année pour l&#8217;équipe
|
||
«cloud-services»&nbsp;francophone!</p>
|
||
<p>Tout d&#8217;abord, nouvelle importante, l&#8217;équipe s&#8217;é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&#8217;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&#8217;échelle <a class="reference external" href="https://github.com/mozilla-services/loop-server">le serveur</a>,
|
||
écrit en Node.js®, pour&nbsp;l&#8217;occasion.</p>
|
||
<div class="section" id="un-serveur-de-listes-de-lecture">
|
||
<h2>Un serveur de listes de&nbsp;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&#8217;idée
|
||
étant de pouvoir marquer des pages &#8220;à lire pour plus tard&#8221; et de continuer la
|
||
lecture sur n&#8217;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&#8217;il est
|
||
possible d&#8217;héberger&nbsp;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&#8217;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&#8217;a pas été pensé pour garantir la pérennité des données,
|
||
et la marche était trop haute pour changer ça en&nbsp;profondeur.</p>
|
||
<p>Nous aurions pu aussi nous contenter de faire une énième application qui expose
|
||
une <span class="caps">API</span> et persiste des données dans une base de&nbsp;données.</p>
|
||
<p>Mais cette nouvelle petite équipe n&#8217;est pas là par hasard&nbsp;:)</p>
|
||
</div>
|
||
<div class="section" id="la-daybed-team">
|
||
<h2>La «Daybed&nbsp;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&#8217;utilisation de
|
||
ce service doit être un jeu d&#8217;enfant ! L&#8217;application gère ses données
|
||
localement (aka offline-first), et synchronise à la&nbsp;demande.</p>
|
||
<p>Ici, le cœur du serveur <em>Reading List</em> est justement une <span class="caps">API</span> &#8220;<span class="caps">CRUD</span>&#8221; (Create,
|
||
Retrieve, Update, Delete), qui gère de la synchronisation et de
|
||
l&#8217;authentification. Nous avons donc pris le parti de faire une <span class="caps">API</span> &#8220;simple&#8221;,
|
||
avec le moins de spécificités possible, qui poserait les bases d&#8217;un service
|
||
générique. Notamment parce qu&#8217;il y a d&#8217;autres projets dans la même trempe qui vont&nbsp;suivre.</p>
|
||
<p>Pas mal d&#8217;expérience ayant été accumulée au sein de l&#8217;équipe, avec d&#8217;une part la
|
||
création de <em>Firefox Sync</em>, et d&#8217;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&nbsp;preuves.</p>
|
||
<p>Par exemple, nous avons conservé le mécanisme de collections d&#8217;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&#8217;étendre légèrement et surtout de le dissocier du projet de listes de&nbsp;lecture.</p>
|
||
</div>
|
||
<div class="section" id="le-mecanisme-qui-force-a-aller-de-l-avant">
|
||
<h2>Le mécanisme qui force à aller de&nbsp;l&#8217;avant</h2>
|
||
<p>Comme première pierre à l&#8217;édifice, nous avons donné naissance au projet
|
||
<a class="reference external" href="https://cliquet.readthedocs.org">Cliquet</a>, dont l&#8217;idée principale est de
|
||
fournir une implémentation de ce protocole en python, tout en factorisant
|
||
l&#8217;ensemble de nos bonnes pratiques (pour la prod&nbsp;notamment).</p>
|
||
<img alt="Logo du projet Cliquet" class="align-right" src="https://blog.notmyidea.org/images/cliquet/cliquet-logo.png" />
|
||
<p>L&#8217;avantage d&#8217;avoir un protocole plutôt qu&#8217;un monolithe, c&#8217;est que si vous
|
||
préférez Asyncio, io.js ou Go, on vous encouragera à publier votre
|
||
implémentation alternative&nbsp;!</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&nbsp;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&#8217;est
|
||
bel et bien en route ! Il permet déjà d&#8217;ê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&#8217;à partir des
|
||
besoins concrets qui surviennent. Ça paraît tout bête, mais sur <em>Daybed</em> on
|
||
l&#8217;a pas vu venir&nbsp;:)</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&nbsp;tutoriaux.</p>
|
||
<p>À bientôt, donc&nbsp;!</p>
|
||
</div>
|
||
</content><category term="code"></category></entry></feed> |