Authentification des utilisateurs Django
Django possède un système d'authentification des utilisateurs. Il permet de gérer des comptes utilisateurs, des groupes, des permissions et des sessions basées sur les cookies. Ce document explique son fonctionnement.
Vue d'ensemble
Le système d'authentification consiste en :
- Utilisateurs
- Permissions : drapeaux booléens (yes/no) indiquant si un utilisateur peut ou non réaliser une certaine tâche
- Groupes : une façon générique d'appliquer des étiquettes et des permissions à plus d'un utilisateur
- Messages : une façon simple d'envoyer des messages à certains utilisateurs.
Installation
Le support d'authentification est inclus en tant qu'application Django dans django.contrib.auth. Pour l'installer, suivez ces instructions :
- Ajoutez django.contrib.auth à la variable INSTALLED_APPS
- Lancez la commande manage.py syncdb.
Notez que dans le fichier settings.py créé par défaut par django-admin.py startproject, la variable INSTALLED_APPS contient déjà django.contrib.auth pour vous faciliter la vie. Si c'est le cas pour vous, relancez la commande manage.py sycndb sans hésiter ; en fait, vous pouvez la lancer aussi souvent que vous le souhaitez, elle n'installera à chaque fois que ce qui est nécessaire.
La commande syncdb crée les tables nécessaires dans la base de données, crée les objets permission pour toutes les applications installées qui en ont besoin, et vous demande de créer un compte superutilisateur la première fois que vous la lancez.
Une fois que vous avez fait tout ça, c'est prêt.
Utilisateurs
Les utilisateurs sont représentés par un modèle Django standard, qui est décrit dans django/contrib/auth/models.py.
Référence de l'API
Champs
Les objets utilisateurs Users possèdent les champs suivant :
- username -- Requis. 30 caractères ou moins. Caractères alphanumériques seulement (lettres, chiffres et tiret de soulignement).
- first_name -- Prénom. Optionnel. 30 caractères ou moins.
- last_name -- Nom. Optionnel. 30 caractères ou moins.
- email -- Optionnel. Adresse de courriel.
- password -- Requis. Version chiffrée du mot de passe et métadonnées relatives à celui-ci (Django ne conserve pas de mot de passe en clair). Le mot de passe peut être d'une longueur quelconque et contenir n'importe quel caractère. Voir la section "Mot de passe" ci-dessous.
- is_staff -- Booléen. Indique si cet utilisateur peut accéder au site d'administration.
- is_active -- Booléen. Indique si ce compte utilisateur peut être utilisé pour se connecter au site. Positionnez ce drapeau à False plutôt que d'effacer un compte utilisateur.
- is_superuser -- Booléen. Indique que cet utilisateur possède toutes les permissions sans les lui assigner explicitement.
- last_login -- Un champ datetime du dernier login de l'utilisateur. Positionné par défaut à la date courante.
- date_joined -- Un champ datetime indiquant la date et l'heure de création de ce compte utilisateur. Positionné par défaut à la date courante lorsque le compte est créé.
Méthodes
Les objets User possèdent deux champs de relation plusieurs-à-plusieurs : groups et user-permissions. Ils peuvent accéder aux objets reliés de la même façon que tous les autres modèles Django:
myuser.groups = [group_list] myuser.groups.add(group, group, ...) myuser.groups.remove(group, group, ...) myuser.groups.clear() myuser.user_permissions = [permission_list] myuser.user_permissions.add(permission, permission, ...) myuser.user_permissions.remove(permission, permission, ...r) myuser.user_permissions.clear()
En plus de ces méthodes automatiques, les objets User possèdent les méthodes spécifiques suivantes :
is_anonymous() -- Renvoie toujours False. C'est une manière de distinguer les objets User et AnonymousUser. En général, vous devriez préférer la méthode is_authenticated à celle-ci.
is_authenticated() -- Renvoie toujours True. C'est une façon de savoir si un utilisateur a été authentifié. Cela n'implique aucune permission, et ne vérifie pas si l'utilisateur est actif - cela indique simplement que l'utilisateur a fourni un nom d'utilisateur et un mot de passe valides.
get_full_name() -- Renvoie le first_name et le last_name, séparés par une espace.
set_password(raw_password) -- Positionne le mot de passe de l'utilisateur à la valeur (chaîne de caractères) indiquée, en prenant en charge de le chiffrement de celle-ci. Ne sauvegarde pas l'objet User.
check_password(raw_password) -- Renvoie True si la chaîne fournie est le mot de passe de l'utilisateur (prend en charge le chiffrement pour faire la comparaison).
set_unusable_password() -- Nouveau dans la version de développement de Django. Indique que l'utilisateur n'a pas de mot de passe. Ce n'est pas la même chose qu'avoir une chaîne vide pour mot de passe. check_password() ne renverra jamais True pour cet utilisateur. Ne sauvegarde pas l'objet User.
Vous pouvez en avoir besoin si l'authentification de votre application se fait au travers d'une source externe existante telle qu'un annuaire LDAP.
has_usable_password() -- Nouveau dans la version de développement de Django. Renvoie False si set_unusable_password() a été appelé pour cet utilisateur.
get_group_permissions() -- Renvoie une liste des permissions que possède l'utilisateur au travers des groupes auxquels il appartient.
get_all_permissions() -- Renvoie une liste de toutes les permissions que possède l'utilisateur, permissions de groupes et permissions utilisateur.
has_perm(perm) -- Renvoie True si l'utilisateur possède la permission spécifiée (au format "package.codename"). Si l'utilisateur est inactif, renvoie toujours False.
has_perms(perm_list) -- Renvoie True si l'utilisateur possède toutes les permissions spécifiées (au format "package.codename"). Si l'utilisateur est inactif, renvoie toujours False.
has_module_perms(package_name) -- Renvoie True si l'utilisateur possède au moins une permission dans le paquet (l'application Django) indiqué. Si l'utilisateur est inactif, renvoie toujours False.
get_and_delete_messages() -- Renvoie une liste d'objets Message en provenance de la file d'attente de l'utilisateur, et les supprime de celle-ci.
email_user(subject, message, from_email=None) -- Envoie un courriel à l'utilisateur. Si la paramètre from_email vaut None, Django utilise la valeur de la variable DEFAULT_FROM_EMAIL.
get_profile() -- Renvoie un profil spécifique au site pour cet utilisateur. Lève l'exception django.contrib.auth.models.SiteProfileNotAvailable si le site courant n'autorise pas les profils. Pour des informations sur les profils utilisateurs spécifiques aux sites, voir la section sur Ajout d'informations supplémentaires sur les utilisateurs ci-dessous.
Gestionnaires
Le modèle User possède un gestionnaire spécifique qui fournit les fonctions suivantes :
create_user(username, email, password=None) -- Crée, sauvegarde et renvoie un objet User. Les champs username, email et password sont positionnés sur les valeurs fournies, et le drapeau is_active est positionné à True pour cet utilisateur.
Si aucun mot de passe n'est fourni, set_unusable_password() sera appelé.
Voir Création d'utilisateurs pour un exemple d'utilisation.
make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789') Renvoie un mot de passe aléatoire de la longueur donnée à partir de la liste des caractères autorisés. Notez que la liste fournie par défaut ne contient de lettres ou chiffres pouvant prêter à confusion comme "I", "1" et "0".
Utilisation de base
Création d'utilisateurs
La façon la plus simple de créer des utilisateurs est d'utiliser la fonction create_user qui est fournie avec Django:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
# user est maintenant un objet User déjà sauvegardé
# dans la base de données. Vous pouvez continuer à changer ses
# attributs si vous voulez changer la valeur d'autres champs.
>>> user.is_staff = True
>>> user.save()
Modifier un mot de passe
Utilisez set_password() pour changer un mot de passe:
>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username__exact='john')
>>> u.set_password('new password')
>>> u.save()
Ne changez pas la valeur de l'attribut password directement à moins de savoir précisément ce que vous faites. Les détails sont expliqués dans la section suivante.
Mots de passe
L'attribut password d'un objet User est une chaîne au format:
hashtype$salt$hash
C'est-à-dire le type de chiffrement (hashtype), la graine (salt) et le mot de passé chiffré (hash), séparés par des dollars.
Le type de chiffrement est sha1 (par défaut), md5 ou crypt -- l'algorithme utilisé pour faire le chiffrage à sens unique du mot de passe. La graine est une chaîne aléatoire utilisée dans la création du mot de passe chiffré. Notez que la méthode crypt n'est supportée que sur les plateformes sur lesquelles le module Python crypt est disponible, et le support de crypt n'existe que dans la version de développement de Django.
Exemple:
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
Les fonctions User.set_password() et User.check_password() gèrent la mise en place et la vérification de ces valeurs pour vous.
Certaines versions antérieures de Django, comme la 0.90, utilisaient de simples chiffres MD5 sans utiliser de graine (salt). Pour assurer la compatibilité ascendante, ils sont toujours supportés mais seront automatiquement convertis en chiffres utilisant la nouvelle méthode la première fois que User.check_password() fonctionnera correctement pour un utilisateur donné.
Utilisateurs anonymes
La classe django.contrib.auth.models.AnonymousUser implémente l'interface django.contrib.auth.models.User, mais avec ces différences :
- id vaut toujours None.
- is_staff et is_superuser sont toujours False.
- is_active est toujours False.
- groups et user_permissions sont toujours vides.
is_anonymous() renvoie True au lieu de False.
is_authenticated() renvoie False au lieu de True.
has_perm() renvoie toujours False.
set_password(), check_password(), save(), delete(), set_groups() et set_permissions() lèvent l'exception NotImplementedError.
En pratique, vous n'aurez probablement pas à utiliser les objets AnonymousUser directement, mais ils sont utilisés par les requêtes web, comme il est expliqué dans la section suivante.
Création de superutilisateurs
manage.py syncdb vous propose de créer un superutilisateur la première fois que vous l'utilisez après avoir ajouté 'django.contrib.auth' à vos INSTALLED_APPS. Mais si vous avez besoin de créer un superutilisateur après cela, vous pouvez utiliser un utilitaire en ligne de commande.
Nouveau dans la version de développement de Django:
manage.py createsuperuser --username=joe --email=joe@example.com
Il vous sera demandé un mot de passe. Après que vous en ayez fourni un, l'utilisateur sera créé immédiatement. Si vous n'utilisez pas les options --username et --email, ces valeurs vous seront demandées.
Si vous utilisez toujours une version plus ancienne de Django, la façon traditionnelle de créer un superutilisateur en ligne de commande fonctionne toujours:
python /path/to/django/contrib/auth/create_superuser.py
...où /path/to est le chemin de la base de code de Django sur votre système de fichiers. La commande manage.py est préférable parce qu'elle détermine les chemins et l'environnement corrects pour vous.
Ajout d'informations supplémentaires sur les utilisateurs
Si vous souhaitez stocker des informations supplémentaires a propos de vos utilisateurs, Django fournit une technique pour spécifier un modèle propre à votre site, en relation avec le modèle User, à cet effet. Ces modèles sont appelés "profils utilisateurs".
Pour utiliser cette fonctionnalité, définissez un modèle contenant les champs nécessaires pour les informations supplémentaires que vous souhaitez conserver, ainsi que les méthodes additionnelles dont vous voulez disposer. Ajoutez également une ForeignKey de votre modèle vers le modèle User, en spécifiant unique=True pour être certain qu'il n'y aura qu'une seule instance de votre modèle pour chaque User.
Pour indiquer que ce modèle est le profil utilisateur pour un site donné, renseignez la variable de réglage AUTH_PROFILE_MODULE avec une chaîne formée des éléments suivant, séparés par des points :
- Le nom de l'application (en minuscules) dans laquelle le modèle de profil utilisateur est défini (en d'autres termes, une version tout en minuscule du nom qui a été passé à manage.py startapp pour créer l'application).
- Le nom de la classe du modèle, en minuscules également.
Par exemple, si le modèle de profil est une classe appelée UserProfile et est défini dans une application appelée comptes, la variable appropriée dans votre fichier de réglages serait:
AUTH_PROFILE_MODULE = 'comptes.userprofile'
Quand un profil utilisateur a été défini et spécifié de cette façon, chaque objet User aura une méthode -- get_profile() -- qui renverra l'instance du modèle de profil utilisateur associée à ce User.
Pour plus d'informations, voir le chapitre 12 du livre de Django.
Authentification au travers des requêtes web
Jusqu'à maintenant, ce document a présenté l'API de bas niveau destinée à manipuler les objets relatifs à l'authentification. A un niveau plus élevé, Django permet d'utiliser ce système d'authentification au travers des objets requêtes.
Avant tout, installez les modules intermédiaires SessionMiddleware et AuthenticationMiddleware en les ajoutant dans la variable MIDDLEWARE_CLASSES. Voir la documentation des sessions pour plus de détails sur ce point.
Une fois que ces modules intermédiaires sont installés, vous pouvez accéder à request.user dans les vues. request.user vous fournit un objet User représentant l'utilisateur actuellement authentifié. Si aucun utilisateur n'est actuellement authentifié, request.user renvoie une instance de AnonymousUser (voir la section précédente). Vous pouvez distinguer l'utilisateur authentifié d'un utilisateur anonyme à l'aide de is_authenticated() de cette façon
if request.user.is_authenticated():
# Traitement d'un utilisateur authentifié.
else:
# Traitement d'un utilisateur anonyme.
Comment authentifier un utilisateur
Django fournit deux fonctions dans django.contrib.auth : authenticate() et login().
Pour authentifier un couple utilisateur-mot de passe donné, utilisez authenticate(). La fonction prend deux arguments, username et password, et renvoie un objet User si le mot de passe est valide pour l'utilisateur donné. Si le mot de passe est invalide, authenticate() renvoie None. Exemple:
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
if user.is_active:
print "You provided a correct username and password!"
else:
print "Your account has been disabled!"
else:
print "Your username and password were incorrect."
Pour authentifier un utilisateur dans une vue, utilisez login(). Cette fonction prend un objet HttpRequest et un objet User. login() sauve l'ID de l'utilisateur dans la session courante en utilisant la gestion des sessions de Django, donc, il faut que le module intermédiaire de gestion des sessions soit installé comme indiqué ci-dessus.
Cet exemple montre comment utiliser authenticate() et login() pour authentifier un utilisateur:
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirection vers une page de succès.
else:
# Renvoyer un message d'erreur 'compte inactif'.
else:
# Renvoyer un message d'erreur 'login invalide'.
Appeler d'abord authenticate()
Quand vous authentifiez manuellement un utilisateur, vous devez appeler authenticate() avant d'appeler login(). authenticate() place un attribut sur le User, indiquant quel backend a pu authentifier cet utilisateur (voir la documentation des backends pour des détails), et cette information est nécessaire plus tard dans le processus de login.
Vérification manuelle du mot de passe d'un utilisateur
Si vous souhaitez authentifier manuellement un utilisateur en comparant un mot de passe clair à la version chiffrée stockée dans la base de données, utilisez la fonction django.contrib.auth.models.check_password. Elle prend deux arguments : le mot de passe en clair à vérifier, et la valeur du champ password de l'utilisateur concerné dans la base de données, et renvoie True s'ils correspondent, False sinon.
Comment déconnecter un utilisateur
Pour déconnecter un utilisateur qui a été authentifié via django.contrib.auth.login(), utilisez django.contrib.auth.logout() dans votre vue. Cette fonction prend un objet HttpRequest et n'a pas de valeur de retour. Exemple:
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirection vers une autre page.
Notez que logout() ne renvoie aucune erreur si l'utilisateur n'était pas authentifié.
Limiter l'accès aux utilisateurs authentifiés
La manière de base
La façon simple et basique de limiter l'accès à une page est de vérifier la valeur de request.user.is_authenticated() et de rediriger vers une page d'accès (login):
from django.http import HttpResponseRedirect
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
# ...
...ou d'envoyer un message d'erreur:
def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
# ...
Le decorator login_required
Toutefois, vous pouvez vous simplifier le travail en utilisant le decorator login_required:
from django.contrib.auth.decorators import login_required
def my_view(request):
# ...
my_view = login_required(my_view)
Un exemple équivalent, en utilisant la syntaxe plus concise introduite dans Python 2.4:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
# ...
Dans la version de développement de Django, login_required a aussi un paramètre optionnel redirect_field_name. Exemple:
from django.contrib.auth.decorators import login_required
def my_view(request):
# ...
my_view = login_required(redirect_field_name='redirect_to')(my_view)
Toujours un exemple équivalent avec la syntaxe plus concise introduite dans Python 2.4:
from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='redirect_to')
def my_view(request):
# ...
login_required fait pour vous les choses suivantes :
- Si l'utilisateur n'est pas authentifié, redirection vers la page settings.LOGIN_URL (accounts/login par défaut), en lui passant l'URL absolue de la page courante dans la requête comme paramètre pour next (ou la valeur de redirect_field_name). Par exemple : /accounts/login/?next=/polls/3/.
- Si l'utilisateur est authentifié, exécute la vue normalement. Le code de la vue peut considérer que l'utilisateur s'est authentifié correctement.
Notez que vous devrez associer la bonne vue à settings.LOGIN_URL. Par exemple, pour utiliser les valeurs par défaut, ajoutez la ligne suivante a votre URLconf:
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
Voici ce que fait django.contrib.auth.views.login :
- Si la vue est appelée par GET, elle affiche un formulaire d'authentification qui enverra les informations par POST sur la même URL. Voir ci-dessous.
- Si la vue est appelée par POST, elle essaie d'authentifier l'utilisateur. En cas de succès, elle redirige vers l'URL spécifiée dans next. Si aucune valeur n'est fournie pour next, elle redirige vers settings.LOGIN_REDIRECT_URL (qui par défaut est /accounts/profile/). Si l'authentification est un échec, elle ré-affiche le formulaire d'authentification.
Il est de votre responsabilité de fournir le formulaire d'authentification dans un template appelé registration/login.html par défaut. Trois variables de contexte sont passées à ce template :
- form : Un objet Form représentant le formulaire d'authentification. Voir la documentation des newforms pour en savoir plus sur les objets Form.
- next : L'URL vers laquelle rediriger si l'authentification est un succès. Peut également contenir une chaîne de requête.
- site_name : Le nom du Site courant, tel que fourni par la variable SITE_ID. Si vous utilisez la version de développement de Django et que vous n'avez pas installé le système de gestion de sites, la valeur sera celle de request.META['SERVER_NAME']. Pour plus d'informations sur le système de gestion de sites, voir la documentation sites.
Si vous préférez appeler le template autrement que registration/login.html, vous pouvez passer le paramètre template_name comme argument supplémentaire à la vue dans votre URLconf. Par exemple, cette ligne d'URLconf utiliserait myapp/login.html à la place:
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
Voici un exemple de template registration/login.html que vous pouvez utiliser comme point de départ. Il suppose que vous avez un template base.html qui définit un bloc content :
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p>Utilisateur et mot de passe incorrects. Essayez à nouveau s'il vous plaît.</p>
{% endif %}
<form method="post" action=".">
<table>
<tr><td>{{ form.username.label_tag }}</td><td>{{ form.username }}</td></tr>
<tr><td>{{ form.password.label_tag }}</td><td>{{ form.password }}</td></tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}
Autres vues prêtes à l'emploi
En plus de la vue login, le système d'authentification contient quelques vues prêtes à l'emploi utiles :
django.contrib.auth.views.logout
Description :
Déconnecte un utilisateur.
Paramètres optionnels :
- template_name: Le nom complet d'un template à afficher après que l'utilisateur se soit déconnecté. La valeur par défaut est registration/logged_out.html si aucun autre argument n'est fourni.
Contexte de template :
- title: La chaîne "Logged out", traduite.
django.contrib.auth.views.logout_then_login
Description :
Déconnecte un utilisateur, puis le redirige vers la page d'authentification.
Paramètres optionnels :
- login_url: L'URL de la page d'authentification. La valeur par défaut est settings.LOGIN_URL.
django.contrib.auth.views.password_change
Description :
Permet à un utilisateur de changer de mot de passe.
Paramètres optionnels :
- template_name: Le nom complet du template à utiliser pour afficher le formulaire de changement de mot de passe. La valeur par défaut est registration/password_change_form.html.
Contexte de template :
- form: Le formulaire de changement de mot de passe.
django.contrib.auth.views.password_change_done
Description :
La page affichée après qu'un utilisateur ait changé de mot de passe.
Paramètres optionnels :
- template_name: Le nom complet du template à utiliser. La valeur par défaut est registration/password_change_done.html.
django.contrib.auth.views.password_reset
Description :
Permet à un utilisateur de réinitialiser son mot de passe, et lui envoie le nouveau mot de passe par courriel.
Paramètres optionnels :
- template_name: Le nom complet du template à utiliser pour afficher le formulaire de réinitialisation du mot de passe. La valeur par défaut est registration/password_reset_form.html.
- email_template_name: Le nom complet du template à utiliser pour générer le courriel avec le nouveau mot de passe. La valeur par défaut est registration/password_reset_email.html.
Contexte de template :
- form: Le formulaire de réinitialisation du mot de passe.
django.contrib.auth.views.password_reset_done
Description :
La page affichée après qu'un utilisateur ait réinitialisé son mot de passe.
Paramètres optionnels :
- template_name: Le nom complet du template à utiliser. La valeur par défaut est registration/password_reset_done.html.
django.contrib.auth.views.redirect_to_login
Description :
Redirige vers la page d'authentification, puis vers une autre URL si l'authentification a été un succès.
Paramètre requis :
- next: L'URL vers laquelle rediriger après un succès.
Paramètres optionnels :
- login_url: L'URL de la page d'authentification vers laquelle rediriger. La valeur par défaut est settings.LOGIN_URL.
Formulaires par défaut
Nouveau dans la version de développement de Django
Si vous ne souhaitez pas utiliser les vues prêtes à l'emploi, mais voulez conserver l'avantage de ne pas avoir à écrire de formulaires pour cette fonctionnalité, le système d'authentification vous fournit plusieurs formulaires :
- django.contrib.auth.forms.AdminPasswordChangeForm: Un formulaire utilisé dans l'interface d'administration pour changer le mot de passe d'un utilisateur.
- django.contrib.auth.forms.AuthenticationForm: Un formulaire pour authentifier un utilisateur
- django.contrib.auth.forms.PasswordChangeForm: Un formulaire pour permettre à un utilisateur de changer de mot de passe.
- django.contrib.auth.forms.PasswordResetForm: Un formulaire pour réinitialiser le mot de passe d'un utilisateur et lui envoyer le nouveau mot de passe par courriel.
- django.contrib.auth.forms.UserCreationForm: Un formulaire pour créer un nouvel utilisateur.
Limiter l'accès aux utilisateurs authentifiés qui satisfont une autre condition
Pour limiter l'accès sur la base de certaines permissions ou d'autres conditions, le principe est le même que celui décrit dans la section précédente.
La façon la plus simple est de tester votre condition sur request.user directement dans la vue. Par exemple, cette vue vérifie que l'utilisateur est authentifié et a la permission polls.can_vote:
def my_view(request):
if not (request.user.is_authenticated() and request.user.has_perm('polls.can_vote')):
return HttpResponse("Vous ne pouvez pas voter dans ce sondage.")
# ...
Vous pouvez utiliser le decorator user_passes_test qui est un raccourci bien pratique:
from django.contrib.auth.decorators import user_passes_test
def my_view(request):
# ...
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view)
On utilise ce test en particulier pour faire un exemple relativement simple. Si toutefois vous voulez simplement savoir si un utilisateur a une permission donnée, utilisez le decorator permission_required() qui est décrit plus loin dans ce document.
Voici la même chose en utilisant la syntaxe de decorator de Python 2.4:
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.has_perm('polls.can_vote'))
def my_view(request):
# ...
user_passes_test a un paramètre obligatoire : un objet appelable qui prend un objet User et renvoie True si l'utilisateur est autorisé à voir la page. Notez que user_passes_test ne vérifie pas automatiquement si l'utilisateur est anonyme ou non.
user_passes_test() a également un paramètre optionnel, login_url, qui vous permet de préciser l'URL de votre page d'authentification (settings.LOGIN_URL par défaut).
Exemple avec la syntaxe de Python 2.3:
from django.contrib.auth.decorators import user_passes_test
def my_view(request):
# ...
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/')(my_view)
Exemple avec la syntaxe de Python 2.4:
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/')
def my_view(request):
# ...
Le decorator permission_required
Il est relativement courant de vérifier si un utilisateur possède une permission donnée. C'est pourquoi Django fournit un utilitaire à cet effet, le decorator permission_required(). En utilisant ce decorator, l'exemple de la section précédente devient:
from django.contrib.auth.decorators import permission_required
def my_view(request):
# ...
my_view = permission_required('polls.can_vote')(my_view)
Notez que permission_required() a aussi le paramètre optionnel login_url. Exemple:
from django.contrib.auth.decorators import permission_required
def my_view(request):
# ...
my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view)
Comme le decorator login_required, login_url a settings.LOGIN_URL pour valeur par défaut.
Limiter l'accès aux vues génériques
Pour limiter l'accès aux vues génériques, encapsulez la vue et faites pointer l'URLconf vers le code qui l'encapsule plutôt que vers la vue elle-même. Par exemple:
from django.views.generic.date_based import object_detail
@login_required
def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs)
Permissions
Django procure un système de permissions simple. Il fournit une façon d'assigner des permissions à des utilisateurs spécifiques, ou à des groupes d'utilisateurs.
Ce système est utilisé par le site d'administration de Django, mais vous pouvez bien sûr l'utiliser dans votre propre code.
Le site d'administration utilise les permissions de la manière suivante:
- L'accès au formulaire d'ajout et l'ajout d'un objet particulier ne sont autorisés qu'aux utilisateurs qui ont la permission "add" pour cet objet.
- L'accès à la liste de modifications, au formulaire de modification et la modification d'un objet ne sont autorisés qu'aux utilisateurs qui ont la permission "change" pour ce type d'objet.
- La possibilité d'effacer un objet n'est autorisée qu'aux utilisateurs qui ont la permission "delete" pour ce type d'objet.
Les permissions sont positionnées globalement par type d'objet, par pour une instance spécifique d'un objet. Par example il est possible de dire "Marie peut modifier les nouveaux articles", mais il n'est pas possible actuellement de dire "Marie peut modifier les nouveaux articles, mais seulement ceux qu'elle a écrits elle-même", ou "Marie ne peut modifier que les articles qui ont un certain statut, date de publication ou ID". Cette dernière fonctionnalité est en discussion dans la communauté des développeurs Django.
Permissions par défaut
Quand django.contrib.auth est listé dans votre réglage INSTALLED_APPS, vous êtes sûrs que trois permissions par défaut -- "add", "change" et "delete" -- sont créées pour chaque modèle Django défini dans vos applications installées.
Ces permissions seront créées lorsque vous lancez manage.py syncdb ; la première fois que vous utilisez syncdb après avoir ajouté django.contrib.auth à INSTALLED_APPS, les permissions par défaut seront créées pour tous les modèles installés précédemment, comme pour tous les nouveaux modèles installés cette fois là. Par la suite, les permissions par défaut seront créées pour les nouveaux modèles chaque fois que vous lancerez manage.py syncdb.
Permissions personnalisées
Pour créer des permissions personnalisées pour un modèle, utilisez l'attribut Meta des modèles permissions.
Ce modèle d'exemple crée trois permissions personnalisées:
class USCitizen(models.Model):
# ...
class Meta:
permissions = (
("can_drive", "Can drive"),
("can_vote", "Can vote in elections"),
("can_drink", "Can drink alcohol"),
)
La seule chose que cela fait en pratique est de créer ces permissions supplémentaires lorsque vous lancez syncdb.
Référence de l'API
Comme les utilisateurs, les permissions sont implémentées dans un modèle Django qui se trouve dans django/contrib/auth/models.py.
Champs
Les objets Permission possèdent les champs suivants :
- name -- Requis. 50 caractères ou moins. Exemple: 'Peut voter'.
- content_type -- Requis. Référence à la table de base de données django_content_type, qui contient une ligne pour chaque modèle Django installé.
- codename -- Requis. 100 caractères ou moins. Exemple: 'peut_voter'.
Méthodes
Les objets Permission ont les mêmes méthodes standards d'accès aux données que les autres modèles Django.
Données d'authentification dans les templates
L'utilisateur actuellement authentifié et ses permissions sont disponibles dans le contexte du template quand vous utilisez RequestContext.
Un peu de technique
Techniquement, ces variables ne sont disponibles dans le contexte du template que si vous utilisez RequestContext et que votre règlage TEMPLATE_CONTEXT_PROCESSORS contient "django.core.context_processors.auth", qui est la valeur par défaut. Pour plus de détails, voir la documentation de RequestContext.
Utilisateurs
L'utilisateur actuellement en ligne, soit une instance de User, soit une instance de AnonymousUser, est stocké dans la variable de template {{ user }}:
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
Permissions
Les permissions de l'utilisateur actuellement en ligne sont stockées dans la variable de template {{ perms }}. C'est une instance de django.core.context_processors.PermWrapper, qui est un proxy de permissions destiné aux templates.
Dans l'objet {{ perms }}, la recherche d'attributs simples est un proxy de User.has_module_perms. Cet exemple afficherait True si l'utilisateur en ligne avait une permission quelconque de l'application foo:
{{ perms.foo }}
La recherche d'attributs à deux niveaux est un proxy de User.has_perm. Cet exemple afficherait True si l'utilisateur en ligne avait la permission foo.can_vote:
{{ perms.foo.can_vote }}
Donc, vous pouvez vérifier les permissions dans des structures {% if %} de templates:
{% if perms.foo %}
<p>You have permission to do something in the foo app.</p>
{% if perms.foo.can_vote %}
<p>You can vote!</p>
{% endif %}
{% if perms.foo.can_drive %}
<p>You can drive!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the foo app.</p>
{% endif %}
Groupes
Les groupes permettent de grouper les utilisateurs par catégorie de façon à attribuer des permissions ou autre label à ces utilisateurs. Un utilisateur peut appartenir à un nombre quelconque de groupes.
Un utilisateur appartenant à un groupe hérite automatiquement de toutes les permissions de ce groupe. Par exemple, si le groupe Editeurs du site possède la permission peut_editer_page_accueil, alors tous les utilisateurs de ce groupe auront cette permission.
Au delà des permissions, les groupes offrent une façon commode de grouper les utilisateurs par catégories pour leur affecter un label ou une fonctionnalité étendue. Par exemple, vous pourriez créer un groupe 'Special users', et écrire du code qui leur autorise l'accès à une partie du site réservée, ou permet d'envoyer un email à ce seul groupe.
Messages
Le système de messages est une façon légère de mettre en file d'attente des messages pour un utilisateur donné.
Un message est associé avec un objet User. Il n'y pas de concept d'expiration ou d'horodatage des messages.
Les messages sont utilisés par l'administration de Django lorsque des actions se sont déroulées correctement. Par exemple, "Le sondage Foo a été créé correctement" est un message.
L'API est simple:
- Pour créer un nouveau message, utilisez user_obj.message_set.create(message='message_text').
- Pour retrouver ou effacer des messages, utilisez user_obj.get_and_delete_messages(), qui renvoie une liste des objets Message dans la file d'attente de l'utilisateur (s'il y en a) et les efface de celle-ci.
Dans cet exemple de vue, le système enregistre un message pour l'utilisateur après avoir créé une playlist:
def create_playlist(request, songs):
# Crée la playlist avec les morceaux (songs) passés en paramètre.
# ...
request.user.message_set.create(message="Votre playlist a été créée correctement.")
return render_to_response("playlists/create.html",
context_instance=RequestContext(request))
Quand vous utilisez RequestContext, l'utilisateur actuellement en ligne et ses messages sont disponibles dans le contexte du template sous la forme de la variable de template {{ messages }}. Voici un exemple de template qui affichera les messages:
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Notez que RequestContext appelle get_and_delete_messages en coulisses, donc tous les messages seront effacés même si vous ne les affichez pas.
Enfin, notez que le système de messages ne fonctionne que pour les utilisateurs répertoriés dans la base de données. Pour envoyer des messages aux utilisateurs anonymes, utilisez le système de sessions/
Autres sources d'authentification
Le système d'authentification de Django est suffisant pour la plupart des cas les plus courants, mais vous pouvez avoir besoin de le raccrocher à un autre système d'authentification, c'est-à-dire une autre source de noms d'utilisateurs et de mots de passe, et/ou d'autres méthodes d'authentification.
Par exemple, votre entreprise pourrait avoir un annuaire LDAP dans lequel sont stockés un nom d'utilisateur et un mot de passe pour chaque salarié. Il est clair qu'il serait très pénible pour les administrateurs réseau et pour les utilisateurs eux-mêmes d'avoir des comptes séparés dans l'annuaire LDAP et dans les applications Django.
Pour cette raison, et pour prendre en charge ce genre de situation, le système d'authentification Django vous permet de lui associer une autre source d'authentification. Vous pouvez outrepasser les schémas de base de données de Django, ou vous pouvez utiliser le système par défaut de Django en tandem avec d'autres systèmes.
Spécifier de nouveaux backends d'authentification
Django gère en coulisses une liste de "backends d'authentification" qu'il utilise pour l'authentification. Lorsque django.contrib.auth.authenticate() est appelé -- comme il est expliqué dans "Comment authentifier un utilisateur" ci-dessus --, Django essaie tous les backends d'authentification de la liste. Si la première méthode d'authentification échoue, Django essaie la seconde, et ainsi de suite, jusqu'à ce qu'il ait essayé tous les backends.
La liste des backends d'authentification à utiliser est précisée dans la variable AUTHENTICATION_BACKENDS. Cette variable est un tuple de chemins Python qui pointent vers des classes Python chargées de l'authentification. Ces classes peuvent se trouver où vous voulez dans votre chemin Python.
Par défaut, la valeur de AUTHENTICATION_BACKENDS est:
('django.contrib.auth.backends.ModelBackend',)
C'est-à-dire le système de base d'authentification, fondé sur la base d'utilisateurs Django.
L'ordre des backends dans le tuple AUTHENTICATION_BACKENDS a une importance dans la mesure où si un couple utilisateur et mot de passe est valide dans plusieurs backends, Django s'arrêtera au premier qui permet de le vérifier.
Comment écrire un backend d'authentification
Un backend d'authentification est une classe qui implémente deux méthodes: get_user(user_id) et authenticate(**credentials).
La méthode get_user prend une user_id -- qui peut être un nom d'utilisateur, une ID dans une base de données ou autre -- et renvoie un objet User.
La méthode authenticate prend en argument les valeurs d'authentification. La plupart du temps, ce sera simplement quelque chose du genre:
class MyBackend:
def authenticate(self, username=None, password=None):
# Vérifie le couple username/password et renvoie un objet User.
Mais elle peut aussi authentifier à partir d'un jeton, comme dans:
class MyBackend:
def authenticate(self, token=None):
# Vérifie la validité du jeton, et renvoie un objet User.
Dans tous les cas, authenticate doit vérifier la validité des données d'authentification fournies, et doit renvoyer un objet User qui correspond à ces données si elles sont valides. Si elles ne sont pas valides, la méthode doit renvoyer None.
Le système d'administration de Django est très fortement lié à l'objet Django User décrit au début de ce document. Pour le moment, la meilleure façon de gérer cette contrainte est de créer un objet User Django pour chacun des objets de votre backend (par exemple votre annuaire LDAP, votre base SQL externe, etc.). Vous pouvez soit écrire un script qui le fait à l'avance, soit prévoir que votre méthode authenticate le fasse la première fois que votre utilisateur se connecte.
Voici un exemple de backend qui authentifie à partir d'un nom d'utilisateur et d'un mot de passe définis dans votre fichier settings.py et crée un objet Django User la première fois que l'utilisateur cherche à se connecter:
from django.conf import settings
from django.contrib.auth.models import User, check_password
class SettingsBackend:
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
Use the login name, and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
"""
def authenticate(self, username=None, password=None):
login_valid = (settings.ADMIN_LOGIN == username)
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
if login_valid and pwd_valid:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# Create a new user. Note that we can set password
# to anything, because it won't be checked; the password
# from settings.py will.
user = User(username=username, password='get from settings.py')
user.is_staff = True
user.is_superuser = True
user.save()
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Gérer les autorisations dans les backends personnalisés
Les backends d'authentification personnalisés peuvent fournir leurs propres permissions.
Le modèle utilisateur déléguera les fonctions de recherche de permissions (get_group_permissions(), get_all_permissions(), has_perm(), et has_module_perms()) a tout backend d'authentification qui implémente ces fonctions.
Les permissions de l'objet User seront un surensemble de toutes les permissions retournées par tous les backends. Django accordera a un utilisateur toute permission accordée par un quelconque des backends.
Le backend d'exemple ci-dessus pourrait mettre en place des permissions pour l'administrateur de façon très simple:
class SettingsBackend:
# ...
def has_perm(self, user_obj, perm):
if user_obj.username == settings.ADMIN_LOGIN:
return True
else:
return False
Ce code donne toutes les permissions à l'utilisateur qui a obtenu l'accès dans l'exemple précédent. Notez que les fonctions d'authentification des backends prennent toutes l'objet User comme argument, et qu'elles acceptent également les mêmes arguments que ceux qui sont fournis aux fonctions associées du User.
Une implémentation complète de système d'autorisation est fournie dans django/contrib/auth/backends.py, qui est le backend par défaut et interroge la table auth_permission la plupart du temps.