gwift-book/source/part-3-data-model/auth.adoc

9.1 KiB
Executable File
Raw Blame History

Authentification

Comme on la vu dans la partie sur le modèle, nous souhaitons que le créateur dune liste puisse retrouver facilement les éléments quil aura créé. Ce dont nous navons pas parlé cependant, cest la manière dont lutilisateur va pouvoir créer son compte et sauthentifier. La documentation est très complète, nous allons essayer de la simplifier au maximum. Accrochez-vous, le sujet peut être complexe.

Mécanisme dauthentification

On peut schématiser le flux dauthentification de la manière suivante :

En gros:

  1. La personne accède à une URL qui est protégée (voir les décorateurs @login_required et le mixin LoginRequiredMixin)

  2. Le framework détecte quil est nécessaire pour la personne de se connecter (grâce à un paramètre type LOGIN_URL)

  3. Le framework présente une page de connexion ou un mécanisme daccès pour la personne (template à définir)

  4. Le framework récupère les informations du formulaire, et les transmets aux différents backends dauthentification, dans lordre

  5. Chaque backend va appliquer la méthode authenticate en cascade, jusquà ce quun backend réponde True ou quaucun ne réponde

  6. La réponse de la méthode authenticate doit être une instance dun utilisateur, tel que définit parmi les paramètres généraux de lapplication.

En résumé (bis):

  1. Une personne souhaite se connecter;

  2. Les backends dauthentification senchaîne jusquà trouver une bonne correspondance. Si aucune correspondance nest trouvée, on envoie la personne sur les roses.

  3. Si OK, on retourne une instance de type current_user, qui pourra être utilisée de manière uniforme dans lapplication.

Ci-dessous, on définit deux backends différents pour mieux comprendre les différentes possibilités:

  1. Une authentification par jeton

  2. Une authentification LDAP

from datetime import datetime

from django.contrib.auth import backends, get_user_model
from django.db.models import Q

from accounts.models import Token  (1)


UserModel = get_user_model()


class TokenBackend(backends.ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        """Authentifie l'utilisateur sur base d'un jeton qu'il a reçu.

        On regarde la date de validité de chaque jeton avant d'autoriser l'accès.
        """
        token = kwargs.get("token", None)

        current_token = Token.objects.filter(token=token, validity_date__gte=datetime.now()).first()

        if current_token:
            user = current_token.user

            current_token.last_used_date = datetime.now()
            current_token.save()

            return user

        return None
  1. Sous-entend quon a bien une classe qui permet daccéder à ces jetons ;-)

from django.contrib.auth import backends, get_user_model

from ldap3 import Server, Connection, ALL
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError

from config import settings


UserModel = get_user_model()


class LdapBackend(backends.ModelBackend):
    """Implémentation du backend LDAP pour la connexion des utilisateurs à l'Active Directory.
    """
    def authenticate(self, request, username=None, password=None, **kwargs):
        """Authentifie l'utilisateur au travers du serveur LDAP.
        """

        ldap_server = Server(settings.LDAP_SERVER, get_info=ALL)
        ldap_connection = Connection(ldap_server, user=username, password=password)

        try:
            if not ldap_connection.bind():
                raise ValueError("Login ou mot de passe incorrect")
        except (LDAPPasswordIsMandatoryError, ValueError) as ldap_exception:
            raise ldap_exception

        user, _ = UserModel.objects.get_or_create(username=username)

On peut résumer le mécanisme dauthentification de la manière suivante:

  • Si vous voulez modifier les informations liées à un utilisateur, orientez-vous vers la modification du modèle. Comme nous le verrons ci-dessous, il existe trois manières de prendre ces modifications en compte. Voir également ici.

  • Si vous souhaitez modifier la manière dont lutilisateur se connecte, alors vous devrez modifier le backend.

Modification du modèle

Dans un premier temps, Django a besoin de manipuler des instances de type django.contrib.auth.User. Cette classe implémente les champs suivants:

  • username

  • first_name

  • last_name

  • email

  • password

  • date_joined.

Dautres champs, comme les groupes auxquels lutilisateur est associé, ses permissions, savoir sil est un super-utilisateur, …​ sont moins pertinents pour le moment. Avec les quelques champs déjà définis ci-dessus, nous avons de quoi identifier correctement nos utilisateurs. Inutile dimplémenter nos propres classes, puisquelles existent déjà :-)

Si vous souhaitez ajouter un champ, il existe trois manières de faire.

Extension du modèle existant

Le plus simple consiste à créer une nouvelle classe, et à faire un lien de type OneToOne vers la classe django.contrib.auth.User. De cette manière, on ne modifie rien à la manière dont Django authentife ses utlisateurs: tout ce quon fait, cest un lien vers une table nouvellement créée, comme on la déjà vu au point […voir lhéritage de modèle]. Lavantage de cette méthode, cest quelle est extrêmement flexible, et quon garde les mécanismes Django standard. Le désavantage, cest que pour avoir toutes les informations de notre utilisateur, on sera obligé deffectuer une jointure sur le base de données, ce qui pourrait avoir des conséquences sur les performances.

Substitution

Avant de commencer, sachez que cette étape doit être effectuée avant la première migration. Le plus simple sera de définir une nouvelle classe héritant de django.contrib.auth.User et de spécifier la classe à utiliser dans votre fichier de paramètres. Si ce paramètre est modifié après que la première migration ait été effectuée, il ne sera pas pris en compte. Tenez-en compte au moment de modéliser votre application.

AUTH_USER_MODEL = 'myapp.MyUser'

Notez bien quil ne faut pas spécifier le package .models dans cette injection de dépendances: le schéma à indiquer est bien <nom de lapplication>.<nom de la classe>.

Backend

Templates

Ce qui nexiste pas par contre, ce sont les vues. Django propose donc tout le mécanisme de gestion des utilisateurs, excepté le visuel (hors administration). En premier lieu, ces paramètres sont fixés dans le fichier `settings <https://docs.djangoproject.com/en/1.8/ref/settings/#auth>`_. On y trouve par exemple les paramètres suivants:

  • LOGIN_REDIRECT_URL: si vous ne spécifiez pas le paramètre next, lutilisateur sera automatiquement redirigé vers cette page.

  • LOGIN_URL: lURL de connexion à utiliser. Par défaut, lutilisateur doit se rendre sur la page /accounts/login.

Social-Authentification

Voir ici : python social auth

Un petit mot sur OAuth

OAuth est un standard libre définissant un ensemble de méthodes à implémenter pour laccès (lautorisation) à une API. Son fonctionnement se base sur un système de jetons (Tokens), attribués par le possesseur de la ressource à laquelle un utilisateur souhaite accéder.

Le client initie la connexion en demandant un jeton au serveur. Ce jeton est ensuite utilisée tout au long de la connexion, pour accéder aux différentes ressources offertes par ce serveur. `wikipedia <http://en.wikipedia.org/wiki/OAuth>`_.

Une introduction à OAuth est disponible ici. Elle introduit le protocole comme étant une valet key, une clé que lon donne à la personne qui va garer votre voiture pendant que vous profitez des mondanités. Cette clé donne un accès à votre voiture, tout en bloquant un ensemble de fonctionnalités. Le principe du protocole est semblable en ce sens: vous vous réservez un accès total à une API, tandis que le système de jetons permet didentifier une personne, tout en lui donnant un accès restreint à votre application.

Lutilisation de jetons permet notamment de définir une durée dutilisation et une portée dutilisation. Lutilisateur dun service A peut par exemple autoriser un service B à accéder à des ressources quil possède, sans pour autant révéler son nom dutilisateur ou son mot de passe.

Lexemple repris au niveau du workflow est le suivant : un utilisateur(trice), Jane, a uploadé des photos sur le site faji.com (A). Elle souhaite les imprimer au travers du site beppa.com (B). Au moment de la commande, le site beppa.com envoie une demande au site faji.com pour accéder aux ressources partagées par Jane. Pour cela, une nouvelle page souvre pour lutilisateur, et lui demande dintroduire sa "pièce didentité". Le site A, ayant reçu une demande de B, mais certifiée par lutilisateur, ouvre alors les ressources et lui permet dy accéder.