Skip to content

4.5. emit users

BastSamson edited this page Jul 6, 2017 · 2 revisions

a) Introduction

Le projet emit-users constitue la majeure partie de la sécurisation de la plate-forme. Elle permet de contrôler l'accès à cette dernière par un système d'authentification et de distinction des différents types d'utilisateurs (utilisateur simple et administrateur).

b) Projet Maven

Le projet Maven emit-users a une structure très simple. L'image suivante présente son architecture :

emit-users.png

Description :

  1. représente le package entities ne possédant qu'une classe java, User. Elle représente l'objet User tel qu'il l'est en base de données.

  2. représente le package services. Certaines classes sont des servlets réalisant des opérations en base, d'autres sont des services appelant ces servlets.

  3. représente toutes les requêtes SQL utilisées par les servlets réalisant des opérations en base.

  4. on retrouve context.xml nécessaire à la connexion à la base de données.

  5. on retrouve web.xml qui comprend par exemple le mapping de toutes les servlets.

  6. on retrouve pom.xml comprenant toute la structure du projet Maven, dont les dependencies, des références à d'autres projets que le projet utilise.

Voici quelques exemples de code extraits du projet emit-users :

Déclaration de servlet

L'extrait de code ci-dessous montre la déclaration de la servlet UserUpdate avec sa requête SQL associée sur le fichier web.xml.

 <servlet>
    <servlet-name>user-update</servlet-name>
    <servlet-class>fr.icam.emit.services.UserUpdate</servlet-class>
    <init-param>
      <param-name>sql-query</param-name>
      <param-value>/fr/icam/emit/queries/user-update.sql</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>user-update</servlet-name>
    <url-pattern>/settings/name</url-pattern>
  </servlet-mapping>

Servlet

La servlet UserUpdate, utilisée pour mettre à jour les informations relatives à un utilisateur

package fr.icam.emit.services;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.github.jeromerocheteau.JdbcUpdateServlet;

public class UserUpdate extends JdbcUpdateServlet<Boolean> {

	private static final long serialVersionUID = 31L;

	@Override
	protected void doFill(PreparedStatement statement, HttpServletRequest request) throws Exception {
		String username = request.getParameter("username");
		String firstname = request.getParameter("firstname");
		String lastname = request.getParameter("lastname");
		statement.setString(1, firstname);
		statement.setString(2, lastname);
		statement.setString(3, username);
	}
	
	@Override
	protected Boolean doMap(HttpServletRequest request, int count, ResultSet resultset) throws Exception {
		return count > 0;
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		Boolean done = this.doProcess(request);
		this.doWrite(done, response.getWriter());
	}
	
}

Requête SQL

Requête SQL user-update.sql associée à cette servlet :

update user
set firstname = ?, lastname = ?
where username = ?;

c) Flux

Cette partie décrit le fonctionnement de chaque élément présent dans le flux de navigation d'un utilisateur.

1.Inscription

Comme il a été dit précédemment, l'utilisateur a besoin de s'inscrire avant de s'identifier sur la plate-forme. Cette inscription se fait par le formulaire décrit dans le tutoriel https://github.com/JeromeRocheteau/emit/wiki/2.-Tutorial#b-authentification.

L'inscription d'un utilisateur à la plate-forme se traduit en base de données par 2 enregistrements :

  • dans la table user : username (login), firstname, lastname, type (le type d'utilisateur, par défaut = 'account')
  • dans la table account : username (login), password (mot de passe encrypté grâce à md5*)

Ces enregistrements sont créés par l'appel de la servlet Register (package fr.icam.emit.services) depuis le javascript de sign-up.html (projet emit-home). Cette servlet appelle elle-même 2 autres servlets, AccountCreate et UserCreate qui créent les enregistrements en base via les 2 requêtes SQL du même nom.

Resgister ne peut créer que des utilisateurs simples. Pour qu'un utilisateur devienne administrateur, il suffit de changer la valeur de user.type à 'manager', directement dans MySQL Workbench.

*voir : https://fr.wikipedia.org/wiki/MD5

2.Authentification

Une fois inscrit, l'utilisateur doit se connecter à la plate-forme : https://github.com/JeromeRocheteau/emit/wiki/2.-Tutorial#b-authentification avec son identifiant et son mot de passe. Encore une fois, c'est une fonction javascript '$http.post' présente dans index.html (projet emit-home) qui fait appel à une servlet. Cette servlet Invoke appelle UserFind, une servlet qui vérifie la présence en base du username entré dans le formulaire puis à UserCheck, qui vérifie que le mot de passe est le bon. Si aucune erreur est levée, Invoke appelle une dernière servlet, PassCreate qui créer un enregistrement dans la table cookie. Cet enregistrement contient le username, issued (date de création), et passphrase (un UUID* généré aléatoirement).

En plus de cet enregistrement en base, un cookie est créé sur le navigateur par le javascript. Ce cookie est visible, une fois authentifié sur la plate-forme, dans les outils de développement (F12 sur Google Chrome).

Cookie 'passphrase'.png

*voir : https://fr.wikipedia.org/wiki/Universal_Unique_Identifier

3.Maintien de la connexion

La présence de l'enregistrement en base dans la table cookie combiné à la présence du cookie sur le navigateur permet d'assurer le maintien de la connexion lors de la navigation et de bloquer certains contenus en fonction du type d'utilisateur. En effet, dans tous les .html, on retrouve le code javascript suivant :

    $scope.passphrase = $cookieStore.get('passphrase');
    $scope.profile = null;
    var profile = function() {
        if ($scope.passphrase) {
	    $http.post("http://172.21.50.3:8080/emit-users/profile?passphrase=" + $scope.passphrase)
	        .then(function(success) {
		    $scope.profile = success.data;
                }, function(error) {
	            $scope.profile = null;
                });
        }
    }

En allant vérifier que le cookie du navigateur obtenu avec $cookieStore.get('passphrase') est le même que celui en base, on peut s'assurer que l'utilisateur est bien connecté et on peut par la même occasion connaître son identité, pour pouvoir ajuster le contenu de la page en fonction de ses droits. La servlet appelée par le code javascript, Profile, en appelle une autre, UserInfo, qui fournit au javascript toutes les infos dont il a besoin comme le nom, le prénom et le type d'utilisateur. Ce dernier va être essentiel au chargement des pages HTML. En effet, selon que l'utilisateur soit connecté ou non et selon le type d'utilisateur qu'il est, des directives AngularJS comme "ng-if" permettent de n'afficher que le contenu souhaité :

    <div class="alert alert-danger" role="alert" ng-if="!passphrase">
        <!-- Contenu affiché si l'utilisateur n'est pas connecté -->
    </div>
    
    <div class="alert alert-danger" role="alert" ng-if="passphrase && profile.type == 'account'">
        <!-- Contenu affiché si l'utilisateur n'est pas connecté en tant qu'administrateur -->
    </div>

4.Déconnexion

Le statut 'déconnecté' d'un utilisateur se fait grâce à deux éléments :

  • absence du cookie de session 'passphrase' : soit par suppression par une fonction javascript '$scope.logout', soit par suppression du cookie par le navigateur (un cookie de session se supprime lorsque la session est terminée, c'est à dire quand toutes les pages du navigateur sont fermées)
  • absence de l'enregistrement 'cookie' en base

Ce statut se produit lorsque l'utilisateur utilise l'option 'log out' (sous 'user' dans la barre de navigation). Cette option déclenche une fonction javascript '$scope.logout' :

    $scope.logout = function() {
        $http.post("http://172.21.50.3:8080/emit-users/revoke?passphrase="+ $scope.passphrase).then(function(success) {
        }, function(error) {
        });
        $cookieStore.remove('passphrase');
        $scope.passphrase = null;
        $window.open("http://172.21.50.3:8080/emit-home/index.html","_self");
    }

Cette fonction supprime dans un premier temps l'enregistrement en BDD du 'passphrase' (table emit.cookie) par le biais de la servlet Revoke, puis le cookie du navigateur. L'utilisateur est ensuite redirigé vers la page d'authentification grâce à '$window.open'.