Comptes utilisateur (Node.js+Express)
Dans ce TD nous allons ajouter des comptes d’utilisateur au jeu de puissance 4 que nous avons commencé à développer aux TDs précédents. Nous allons nous servir de la base de données MySQL fournie par Cloud 9 pour le stockage des données côté server, et du système de sessions de Express pour garder l’état de la connexion.
Les références pour ce TD sont :
- Le cours,
- La doc de Express (Connect) sur les sessions,
- La doc de
mysql
, - La référence MySQL.
Préparer son espace de travail
Comme d’habitude, commencez par taper dans le terminal
git pull
npm install mysql
mysql-ctl start
La première commande met à jour l’espace de travail, la troisième lance le server MySQL. Vous pouvez arrêter à tout moment le server MySQL avec
mysql-ctl stop
et le relancer avec
mysql-ctl restart
Si vous n’arrêtez pas le serveur à la fin de la session, il sera encore actif lors de votre prochaine connexion à l’espace de travail.
Un dossier nommé sqlbuddy
est présent dans votre espace de travail,
il s’agit d’un gestionnaire graphique de bases de donnés,
SQL Buddy, similaire à
PHPMyAdmin.
Pour exécuter l’application, ouvrez le fichier sqlbuddy/index.php
et
cliquez sur le bouton « Run ».
Note : Pour lancer plus rapidement SQL Buddy, vous pouvez créer un
profil d’exécution. Avec le fichier sqlbuddy/index.php
ouvert,
allez dans l’onglet « Run & Debug » (icône ), ajoutez un nouveau profil (bouton +), et
remplissez les champs comme suit :
- « Name » : choisissez le nom que vous voulez, par exemple SQL Buddy,
- « File path » :
/sqlbuddy/index.php
(cela devrait être déjà rempli), - « Runtime » : Apache+PHP.
Maintenant vous pouvez lancer SQL Buddy simplement en ouvrant le menu
dropdown à côté du bouton « Run », sans besoin d’ouvrir le fichier
index.php
.
-
Lancez SQL Buddy (il n’y a pas besoin de mot de passe). Créez un nouveau database, et dans ce database une table nommée
users
. La table doit contenir les champs suivants :login
: typevarchar(255)
, clef primaire,pass
: typevarchar(255) NOT NULL
,couleur1
: typevarchar(255)
,couleur2
: typevarchar(255)
,parties
: typeint
, non signé,gagnees
: typeint
, non signé.
-
Ajoutez deux utilisateurs à la table, avec les valeurs que vous voudrez.
Nous sommes maintenant prêts à étendre notre jeu de Puissace 4. Chacune des sections qui suivantes est consacrée à une page de l’application (une vue, en jargon).
Le point d’entrée de l’application va encore être le fichier
app.js
. Vous êtes cependant libres de distribuer votre logique sur
plusieurs fichiers, importés dans app.js
avec la directive require
(voir la doc de Node.js sur les
modules).
Liste des utilisateurs
Cette vue est la plus simple : elle permet d’afficher la liste de tous
les utilisateurs. On rappelle que, avant de pouvoir accéder à la base
de données, il est nécessaire de configurer le module mysql
. Le code
suivant permet de configurer votre application avec les paramètres de
C9.
var mysql = require('mysql');
var db = mysql.createConnection({
host : process.env.IP, // pas touche à ça : spécifique pour C9 !
user : process.env.C9_USER, // vous pouvez mettre votre login à la place
password : '',
database : 'c9' // mettez ici le nom de la base de données
});
Ensuite le DBAL sera accessible via l’objet db
.
-
Dans
app.js
ajoutez une route pour l’URL/userlist
. Elle doit récupérer l’ensemble des lignes de la tableusers
, et en afficher les colonneslogin
,parties
,gagnees
etcouleur1
sous forme de tableau HTML. On rappelle que le résultat de la requête est passé à la callback de la méthodequery
. -
Remplacez le texte de la case correspondante à
couleur1
par un rectangle coloré de cette même couleur. Si la couleur vautNULL
, utilisez une couleur par défaut. Le résultat pourrait ressembler à cela.Joueur Parties Gagnées Colueur préférée Kasparov 10 3 Karpov 100 99
Création des utilisateurs
Nous passons maintenant à une vue qui permet d’ajouter des utilisateurs à la table. Ceci va nous permettre de nous familiariser avec des fonctionnalités avancées de la DBAL.
-
Ajoutez une route de type GET pour l’URL
/signup
. Elle doit présenter un formulaire d’enregistrement permettant de renseigner un login, un mot de passe, une couleur préférée et une couleur secondaire. Le formulaire doit utiliser la méthode POST et renvoyer sur cette même URL (laisser le champsaction
vide suffit). -
Modifiez la route
/signup
pour accepter aussi des requêtes de type POST (rappel : utiliserapp.use()
). Vous pouvez tester le type de la requête avecreq.method
.Lorsque la requête est de type POST, récupérez les valeurs du formulaire et insérez une nouvelle ligne dans la table
users
, seulement si le login et le mot de passe ne sont pas vides (les colonnesparties
etgagnees
doivent démarrer à zéro).Pour réaliser une insertion on utilise la méthode
query
comme pour toute autre requêtedb.query('INSERT INTO users VALUES (?, ?, ...)', [...], function(err, result) { ... });
Si l’insertion réussit, le paramètre
result
contient des informations sur la requête (nombre de lignes insérés, etc., vous pouvez l’examiner avecconsole.log
) ; si l’insertion échoue, il estundefined
.- Si l’insertion de l’utilisateur a réussi, rédirigez vers l’URL
/userlist
(avecapp.redirect
). - Si l’insertion a raté, présentez à nouveau le formulaire, avec un message d’erreur. Ne faites pas de rédirection dans ce cas ; un template vous permettra d’afficher le message d’erreur seulement si nécessaire.
Testez et observez les échanges de requêtes avec l’outil « Réseau » de votre browser.
- Si l’insertion de l’utilisateur a réussi, rédirigez vers l’URL
-
Tester uniquement
result
ne donne pas assez d’information. Essayez, par exemple, d’insérer un utilisateur qui existe déjà, et observez les valeurs deerr
etresult
(dans la console).Repérez le code d’erreur MySQL qui correspond à un login dupliqué. Faites en sorte que votre gestionnaire affiche un message d’erreur significatif lorsque l’utilisateur demande un login déjà existant.
Connexion et jeu
Maintenant nous pouvons passer à l’authentification. On peut voir cela
comme une seule vue (le jeu à proprement parler) utilisant deux URLs :
/
et /play
.
À ce stade il devient nécessaire de garder chez le server l’information que les utilisateurs se sont correctement identifiés : en effet, on veut que le mot de passe soit saisi une seule fois au début d’une série de parties. C’est pourquoi entrent en jeu les sessions.
Comme on a déjà vu en cours, avant de pouvoir utiliser les sessions il faut configurer l’application avec
app
.use(express.cookieParser())
.use(express.session( { secret : '12345' } ));
Ensuite la session sera disponible dans l’objet req.session
.
-
Modifiez le gestionnaire de l’URL
/
pour qu’il affiche un formulaire similaire à celui ciJoueur 1 : mot de passe : Joueur 2 : mot de passe : -
Comme pour l’URL
/signup
, le gestionnaire pour/
se comportera différemment selon si la requête est de type GET ou POST. Dans le deuxième cas, il doit vérifier avec la BD que les deux couples login/password sont corrects, et, en cas affirmatif,- Stocker dans la session les logins des deux utilisateurs, leur scores et leurs couleurs préférées ;
- Rédiriger vers
/play
.
-
Modifiez le gestionnaire de
/play
pour qu’il récupère les noms des joueurs, leurs scores et leurs couleurs dans la session. Si les deux joueurs ont la même couleur préférée, l’un des deux se verra attribuer sa couleur secondaire. -
Modifiez le gestionnaire de
/play
pour que, à la fin de chaque partie, il envoie l’information sur le gagnant au server. Le server stocke cette information dans la table (incrémenteparties
pour chaque joueur, etgagnees
uniquement pour le gagnant).
Améliorations (optionnel)
-
Ajoutez une route
/logout
permettant de terminer une série de parties. Pour cela, il suffit de mettre une valeur spéciale dans la session (par exemple, les logins des joueurs ànull
), et tester cette valeur dans/play
. -
Dans
/signup
, demandez deux fois le mot de passe, et inscrivez le nouveau joueur seulement si les mots de passe coïncident. -
Une mesure de sécurité souvent appliquée consiste à ne jamais stocker les mots de passe en clair dans la base de données. À la place, on stocke le haché (par exemple le SHA1) de la concaténation du mot de passe et d’une graine aléatoire propre à l’application.
Tout le calcul doit se faire côté server. En effet, premièrement la graine ne doit être connue que par le server, deuxièmement, si c’est le client qui fait le calcul, le haché devient effectivement le mot de passe à la place de l’original.
De cette façon, même l’administrateur ne peut pas connaître les mots de passe d’origine. En cas d’oubli du mot de passe, la seule solution est de ré-initialiser l’utilisateur. Un méprise de ce principe est à la base du fameux leak de 150M de mots de passe de Adobe en 2013 : http://zed0.co.uk/crossword/.
Implantez (correctement) ce principe dans votre application. La fonction de hachage SHA1 est disponible sous le nom
sha1()
en PHP et sous le nomSHA1()
en MySQL. -
Dans la page d’accueil, faites en sorte que les identifiants des joueurs soient auto-complétés. Voir https://developer.mozilla.org/docs/Web/HTML/Element/datalist.
-
Enrichissez les données sur les utilisateurs. Au minimum, cela pourrait se limiter au nom et prénom des joueurs. Vous pouvez pousser cela jusqu’à avoir un profil d’utilisateur complet de photos, etc.