Makina Blog
Combiner une authentification LDAP et l'authentification classique Django
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', )
- Suivez-nous sur Twitter : @makina_corpus, @__fle__
- This post in english here : Combine LDAP and classical authentication in django
Formations associées
Formations Django
Formation Django avancé
À distance (FOAD) Du 9 au 13 décembre 2024
Voir la formationActualités en lien
Utiliser des fonctions PostgreSQL dans des contraintes Django
Cet article vous présente comment utiliser les fonctions et les check constraints
PostgreSQL en tant que contrainte sur vos modèles Django.
Comment migrer vers une version récente de Django ?
Que ce soit pour avoir les dernières fonctionnalités ou les correctifs de sécurité, rester sur une version récente de Django est important pour la pérennité de son projet.
Le projet Agrégateur : fusionner des bases de données Geotrek
Le partage et la diffusion des données font partie des problématiques historiques au cœur du projet Geotrek.