Accueil / Blog / Métier / 2014 / Combiner une authentification LDAP et l'authentification classique Django

Combiner une authentification LDAP et l'authentification classique Django

Par Florent Lebreton publié 10/01/2014
Contributeurs : Sylvain Boureliou
Comment mixer une authentification LDAP et l'authentification classique Django pour pouvoir garder la possibilité d'avoir des utilisateurs seulement dans Django et pouvoir pallier une éventuelle défaillance de l'annuaire LDAP ?

Toujours pour notre projet de GMAO full-web JOB, nous avons eu besoin de fournir un système d'authentification via un annuaire LDAP. Le module django-auth-ldap fait une grosse partie du travail lui-même mais nous avons décidé de combiner cette authentification LDAP avec l'authentification classique de Django pour deux raisons :

  • garder la possibilité de créer des utilisateurs dans Django qui n'ont pas à exister dans l'annuaire LDAP (pour les superusers par exemple) ;
  • être capable de se replier sur l'authentification Django pour tous les utilisateurs en cas d'une défaillance du serveur LDAP.

Pour cela, nous devons :

  • pouvoir différencier les utilisateurs provenant de l'annuaire LDAP des utilisateurs Django ;
  • stocker le mot de passe des utilisateurs LDAP dans la base de données Django pour pouvoir basculer sur l'authentification classique si besoin ;
  • écrire deux backends d'authentification personnalisés.

 

User model Django

Nous implémentons simplement un custom model user avec un champ ``from_ldap`` pour différencier les utilisateurs LDAP des utilisateurs Django classiques.

    class MyUser(AbstractUser):
        from_ldap = models.BooleanField(
            _('LDAP user'),
            editable=False,
            default=False)

 

Backend d'authentification LDAP

Ce backend LDAP a deux objectifs :

  • enregistrer le mot de passe de l'utilisateur dans la base de données Django, ainsi l'utilisateur pourra se connecter via le backend d'authentification classique lorsque le backend LDAP est désactivé ;
  • forcer le champ ``from_ldap`` à ``True`` quand un utilisateur est créé par ce biais.
    from django_auth_ldap.backend import LDAPBackend
    from django.contrib.auth import get_user_model

    class MyLDAPBackend(LDAPBackend):
        """ A custom LDAP authentication backend """

        def authenticate(self, username, password):
            """ Overrides LDAPBackend.authenticate to save user password in django """

            user = LDAPBackend.authenticate(self, username, password)

            # If user has successfully logged, save his password in django database
            if user:
                user.set_password(password)
                user.save()

            return user

        def get_or_create_user(self, username, ldap_user):
            """ Overrides LDAPBackend.get_or_create_user to force from_ldap to True """
            kwargs = {
                'username': username,
                'defaults': {'from_ldap': True}
            }
            user_model = get_user_model()
            return user_model.objects.get_or_create(**kwargs)

 

Backend d'authentification classique

Nous surchargeons django.contrib.auth.backends.ModelBackend pour s'assurer que les utilisateurs LDAP ne peuvent pas se connecter via ce backend tant que le backend LDAP est actif.

    from django.contrib.auth import get_backends, get_user_model
    from django.contrib.auth.backends import ModelBackend

    class MyAuthBackend(ModelBackend):
        """ A custom authentication backend overriding django ModelBackend """

        @staticmethod
        def _is_ldap_backend_activated():
            """ Returns True if MyLDAPBackend is activated """
            return MyLDAPBackend in [b.__class__ for b in get_backends()]

        def authenticate(self, username, password):
            """ Overrides ModelBackend to refuse LDAP users if MyLDAPBackend is activated """

            if self._is_ldap_backend_activated():
                user_model = get_user_model()
                try:
                    user_model.objects.get(username=username, from_ldap=False)
                except:
                    return None

            user = ModelBackend.authenticate(self, username, password)

            return user

 

Settings Django et solution de repli

En temps normal, nos deux backends sont activés :

  • les utilisateurs LDAP peuvent se connecter uniquement via MyLDAPBackend ;
  • les utilisateurs Django peuvent se connecter via MyAuthBackend.
    AUTHENTICATION_BACKENDS = (
        'accounts.backends.MyLDAPBackend',
        'accounts.backends.MyAuthBackend',
    )

 En cas de défaillance de l'annuaire LDAP, nous avons juste à désactiver MyLDAPBackend et tout le monde peut se connecter avec MyAuthBackend :

    AUTHENTICATION_BACKENDS = (
        #'accounts.backends.MyLDAPBackend',
        'accounts.backends.MyAuthBackend',
    )

 

 

ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Créer un tag d'inclusion avec paramètres dans Django 22/12/2020

La bibliothèque de tags interne permet d'enregistrer des tags avec paramètres ou des tags ...

Présentation de django-admin-watchdog Présentation de django-admin-watchdog 12/11/2020

Comment garder une trace des erreurs Django en toute simplicité.

Présentation de django-tracking-fields Présentation de django-tracking-fields 03/11/2020

Suivi de modification d'objets Django

Présentation de Django-Safedelete Présentation de Django-Safedelete 09/07/2013

Masquage d'objets en base de données une alternative à la suppression définitive.

Wagtail : Comment écrire les templates (partie 3) Wagtail : Comment écrire les templates (partie 3) 18/07/2016

Il n'y a pas de vue à proprement parlé dans Wagtail. Tout est en fait géré dans le modèle. ...