Site original : Sam & Max: Python, Django, Git et du cul
L’authentification de mémé (enfin, mémé…) n’a plus de sens, et pourtant, on continue de la montrer en exemple dans tous les tutos :
Boom, on vérifie le mot de passe (avec un peu de chance sur une version sainement hachée), on identifie l’utilisateur, et c’est plié.
C’est simple, c’est beau, ça marche.
Mais ça ne représente pas du tout la réalité.
Une des plus grosses erreurs de design de frameworks comme Django est justement la classe User
.
En soit, la classe User
n’est pas un problème, mais son omniprésence dans toutes les couches du framework et sa promotion au rang d’unité de référence dans les apps tierces parties ont gardé la vision de l’authentification très monolithique.
C’est que le Web n’a plus du tout la forme qu’il avait il y a 10 ans, et aujourd’hui, vous pouvez vous connecter via un service (facebook, twitter, github…), utiliser une authentification à double facteur (sms, email, yubikey…) ou avoir un compte unique qui unifie plusieurs fournisseurs (ah, le fameux compte Google qui fait gmail, youtube, calendar, play, saucisseland…).
Les gens ont plusieurs identités, plusieurs comptes, plusieurs modes d’authentification, plusieurs points d’entrées. Ce modèle ne marche plus :
USER <-- Profile
Si vous créez un service aujourd'hui et que vous souhaitez prendre en compte cette réalité, le schéma en base de données va plutôt ressembler à un truc du genre :
+-------+ |Session| +----+--+ | | | | +-----------+ +----v---+ +--------+ | Identity | <-------+ Account| <-------+Profile | +-----------+ +--------+ +--------+ +------^ ^--------+ | | | | | | | | +-------+-----+ +------+----+ | Credential | | Service | +-------------+ +-----------+
Et encore, je n'ai pas mis les permissions, les devices, les comptes multi-utilisateurs, la vérification des comptes (lien email, sms, etc), les niveaux d'offre commerciale... Ca rend le truc vraiment compliqué.
Ce qui est important ici, c'est que la notion de compte devient centrale, mais que seule l'identité est unique. Une identité peut avoir plusieurs comptes. Et un compte peut être chez votre service ou un service externe. Et l'authentification peut très bien avoir lieu ailleurs : ldap, OAuth, Mozilla Personna, Open ID...
Si votre utilisateur a un compte chez Facebook et un compte chez vous, les deux sont des comptes, avec leurs profils, reliés à la même identité, servant à vous donner des informations, permettant de se loguer... Et vous devez garder une trace de tout ça. Le fait que l'API d'un service tape sur votre serveur et l'autre non n'a en fait aucune importance pour l'utilisateur, qui utilise tout ça comme un gros blob comme si internet était un tout.
La nouvelle génération ne fait pas la différence avec un site Web et un app, ne sait pas ce qu'est une URL ou un cookie et ne veut pas rentrer trop de mots de passe, mais veut par contre utiliser 5000 services.
Ainsi on voit que les archis basées sur des microservices (hello crossbar :)) commencent à avoir la côte, faisant abstraction de la notion de "ceci est mon app, ceci est ton app" dans la structure de son projet. Bien entendu, la distinction est importante d'un point de vue humain, mais du point de vue du workflow d'authentification, non.
Par contre, forcément, ça fait beaucoup plus qu'une table User
et une table Profile
en base de données, et c'est pourquoi toutes les apps pluggables des frameworks actuellement ont leurs propres structures bizarres, incompatibles et torturées pour faire tenir toute cette logique. Parce qu'on code sur des frameworks qui vivent encore dans les années 2000, qui passent à votre vue un objet User
en paramètre avec ses attributs username
et email
.
Mais ce que vous avez en face de vous, ce n'est pas un utilisateur.
C'est une session d'utilisation.
D'un service (le votre). Liée à un compte. Qui peut-être (surement) possède un profil avec un username.
Cette session s'est initiée par une authentification avec une méthode parmi 100 auprès d'un service également, lié à un compte, avec un profil. Peut-être le même service que celui qui est utilisé, peut-être pas.
Et cette authentification a pu être demandée par un utilisateur, ou encore un autre service, ou un logiciel client complètement automatiquement. Souvenez-vous : la session n'a peut-être aucun humain derrière.
Ce qui est en face de vous n'est pas un utilisateur. L'utilisateur est mort il y a des années.
Il y a des tas de choses qu’on peut vouloir faire au moment où un fichier change :
Et ce n’est même pas dur à faire en Python grace à la lib watchdog :
pip install watchdog |
D’abord, on créer un handler, une classe qui va contenir le code à lancer quand il arrive quelque chose à nos fichiers :
from watchdog.events import FileSystemEventHandler class MonHandler(FileSystemEventHandler): # cette méthode sera appelée à chaque fois qu'un fichier # est modifié def on_modified(self, event): print("Ah, le fichier %s a été modifé" % event.src_path) # On peut aussi implémenter les méthodes suivantes : # - on_any_event(self, event) # - on_moved(self, event) # - on_created(self, event) # - on_deleted(self, event) # - on_modified(self, event) |
Ensuite on créé un observer, qui va lancer un thread dans lequel il va… observer :
from watchdog.observers import Observer observer = Observer() # Surveiller récursivement tous les événements du dossier /tmp # et appeler les méthodes de MonHandler quand quelque chose # se produit observer.schedule(MonHandler(), path='/tmp', recursive=True) |
On démarre tout ça :
import time observer.start() # L'observer travaille dans un thread séparé donc on fait une # boucle infinie pour maintenir le thread principal # actif dans cette démo mais dans un vrai programme, # vous mettez votre taff ici. try: while True: time.sleep(1) except KeyboardInterrupt: # Ctrl + C arrête tout observer.stop() # on attend que tous les threads se terminent proprement observer.join() |
Pour les ones shot, watchdog vient avec la commande watchmedo
qui permet de lancer un commande shell en cas d’événement :
# lancer 'echo lefichier' pour chaque fichier python ou texte modifié de mon dossier utilisateur watchmedo shell-command --patterns="*.py;*.txt" --recursive --command='echo "${watch_src_path}"' /home/sam |
Personnellement j’aime le lundi.
>>> import crossbar >>> crossbar.__version__ '0.10.4' >>> import twisted >>> twisted.__version__ '15.2.1' >>> import sys >>> print('Wait for it...') Wait for it... >>> sys.version '3.4.0 (default, Apr 11 2014, 13:05:11) \n[GCC 4.8.2]' |
On a décidé de commander quelques trucs pour bidouiller un raspberry PI sur un site américain.
Grâce au Tracking chez UPS on peut savoir où se balade notre colis.
C’est assez cocasse…
Apparement le colis est arrivé dans notre ville mais a du repartir à Paris pour enfin revenir demain ou…
Heureusement que c’est pas un bon Maroilles bien vieux.
Python 3.5 est en phase beta, et c’est une release très (mais alors très très très) attendue.
Si vous ne voulez pas attendre et tester le bouzin de suite, il est facile de setup la bestiole sous linux :
Un exemple possible :
cd /tmp sudo apt-get install python-dev gcc # ou yum install python-devel gcc wget https://www.python.org/ftp/python/3.5.0/Python-3.5.0b3.tgz tar -xvf Python-3.5.0b3.tgz cd Python-3.5.0b3 ./configure make sudo make altinstall |
Ne faites surtout PAS sudo make install
, qui écraserait votre python principal.
Ensuite on lance :
$ python3.5 $ python3.5 Python 3.5.0b3 (default, Jul 5 2015, 23:52:07) [GCC 4.8.4] on linux Type "help", "copyright", "credits" or "license" for more information. Traceback (most recent call last): File "/home/sam/Scripts/pythonstartup.py", line 119, in store = Store(os.path.join(TEMP_DIR, 'store.%s.db') % python_version) File "/home/sam/Scripts/pythonstartup.py", line 93, in __init__ object.__setattr__(self, 'DICT', shelve.DbfilenameShelf(filename)) File "/usr/local/lib/python3.5/shelve.py", line 227, in __init__ Shelf.__init__(self, dbm.open(filename, flag), protocol, writeback) File "/usr/local/lib/python3.5/dbm/__init__.py", line 91, in open "available".format(result)) dbm.error: db type is dbm.gnu, but the module is not available >>> import types >>> print(':)') :) >>> |
En dehors du vomi d’intro, tout marche chez moi.