Una vez que ya tienes un usuario (normalmente mediante request.user
, pero
también puede ser por otros métodos, que se describirán en breve) dispondrás
de una serie de campos de datos y métodos asociados al mismo. Los objetos de
la clase AnonymousUser
emulan parte de esta interfaz, pero no toda, por lo
que es preferible comprobar el resultado de user.is_authenticated()
antes
de asumir de buena fe que nos encontramos ante un usuario legítimo.
Campos de los objetos User
:
username
(obligatorio) 30 caracteres como máximo. Sólo acepta caracteres alfanuméricos (letras, dígitos y el carácter subrayado).first_name
(opcional) 30 caracteres como máximo.last_name
(opcional) 30 caracteres como máximo.email
(opcional). Dirección de correo electrónico.password
(obligatorio). Un código de comprobación (hash), junto con otros metadatos de la contraseña. Django nunca almacena la contraseña en crudo. Véase la sección "Cambia contraseñas" para más información.is_staff
(booleano). Indica que el usuario puede acceder a las secciones de administración.is_active
(booleano). Indica que la cuenta puede ser usada para identificarse. Se puede poner aFalse
para deshabilitar a un usuario sin tener que borrarlo de la tabla.is_superuser
(booleano). Señala que el usuario tiene todos los permisos, aún cuando no se le hayan asignado explícitamente.last_login
. Fecha y hora de la última vez que el usuario se identificó. Se asigna automáticamente a la fecha actual por defecto.date_joined
Fecha y hora en que fue creada esta cuenta de usuario. Se asigna automáticamente a la fecha actual en su momento.
Métodos de los objetos User
:
is_authenticated()
: siempre devuelveTrue
para usuarios reales. Es una forma de determinar si el usuario se ha identificado. esto no implica que posea ningún permiso, y tampoco comprueba que la cuenta esté activa. Sólo indica que el usuario se ha identificado con éxito.is_anonymous()
: devuelveTrue
sólo para usuarios anónimos, yFalse
para usuarios "reales". En general, es preferible usar el métodois_authenticated()
.get_full_name()
: devuelve la concatenación de los camposfirst_name
ylast_name
, con un espacio en medio.set_password(passwd)
: cambia la contraseña del usuario a la cadena de texto en claro indicada, realizando internamente las operaciones necesarias para calcular el código de comprobación o hash necesario. Este método no guarda el objetoUser
.check_password(passwd)
: devuelveTrue
si la cadena de texto en claro que se le pasa coincide con la contraseña del usuario. Realiza internamente las operaciones necesarias para calcular los códigos de comprobación o hash necesarios.get_group_permissions()
: devuelve una lista con los permisos que tiene un usuario, obtenidos a través del grupo o grupos a las que pertenezca.get_all_permissions()
: devuelve una lista con los permisos que tiene concedidos un usuario, ya sea a través de los grupos a los que pertenece o bien asignados directamente.has_perm(perm)
: devuelveTrue
si el usuario tiene el permiso indicado. El valor deperm
está en el formato"package.codename"
. Si el usuario no está activo, siempre devolveráFalse
.has_perms(perm_list)
: devuelveTrue
si el usuario tiene todos los permisos indicados. Si el usuario no está activo, siempre devolveráFalse
.has_module_perms(app_label)
: devuelveTrue
si el usuario tiene algún permiso en la etiqueta de aplicación indicada,app_label
. Si el usuario no está activo, siempre devolveráFalse
.get_and_delete_messages()
: devuelve una lista de mensajes (objetos de la claseMessage
) de la cola del usuario, y los borra posteriormente.email_user(subj, msg)
: envía un correo electrónico al usuario. El mensaje aparece como enviado desde la dirección indicada en el valorDEFAULT_FROM_EMAIL
. Se le puede pasar un tercer parámetro opcional,from_email
, para indicar otra dirección de remite distinta.get_profile()
: devuelve un perfil del usuario específico para el sitio. En la sección "Perfiles" se explicará con más detalle este método.
Por último, los objetos de tipo User
mantienen dos campos de relaciones
múltiples o muchos-a-muchos: Grupos y permisos (groups
y permissions
). Se
puede acceder a estos objetos relacionados de la misma manera en que se usan
otros campos múltiples:
# Set a user's groups:
myuser.groups = group_list
# Add a user to some groups:
myuser.groups.add(group1, group2,...)
# Remove a user from some groups:
myuser.groups.remove(group1, group2,...)
# Remove a user from all groups:
myuser.groups.clear()
# Permissions work the same way
myuser.permissions = permission_list
myuser.permissions.add(permission1, permission2, ...)
myuser.permissions.remove(permission1, permission2, ...)
myuser.permissions.clear()
12.4.1. Iniciar y cerrar sesión
Django proporciona vistas predefinidas para gestionar la entrada
del usuario, (el momento en que se identifica), y la salida, (es
decir, cuando cierra la sesión), además de otros trucos ingeniosos. Pero
antes de entrar en detalles, veremos como hacer que los usuario puedan
iniciar y cerrar la sesión "a mano". Django incluye dos funciones
para realizar estas acciones, en el módulo django.contrib.auth
:
authenticate()
y login()
.
Para autentificar un identificador de usuario y una contraseña, se utiliza
la función authenticate()
. esta función acepta dos parámetros ,
username
y password
, y devuelve un objeto de tipo User
si la
contraseña es correcta para el identificador de usuario. Si falla la
comprobación (ya sea porque sea incorrecta la contraseña o porque sea
incorrecta la identificación del usuario), la función devolverá None
:
>>> from django.contrib import auth
>>> user = auth.authenticate(username='john', password='secret')
>>> if user is not None:
... print "Correct!"
... else:
... print "Oops, that's wrong!"
La llamada a authenticate()
sólo verifica las credenciales del
usuario. Todavía hay que realizar una llamada a login()
para
completar el inicio de sesión. La llamada a login()
acepta un
objeto de la clase HttpRequest
y un objeto User
y almacena
el identificador del usuario en la sesión, usando el entorno de
sesiones de Django.
El siguiente ejemplo muestra el uso de ambas funciones, authenticate()
y
login()
, dentro de una vista:
from django.contrib import auth
def login(request):
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
# Correct password, and the user is marked "active"
auth.login(request, user)
# Redirect to a success page.
return HttpResponseRedirect("/account/loggedin/")
else:
# Show an error page
return HttpResponseRedirect("/account/invalid/")
Para cerrar la sesión, se puede llamar a django.contrib.auth.logout()
dentro de una vista. Necesita que se le pase como parámetro un objeto
de tipo HttpRequest
, y no devuelve ningún valor:
from django.contrib import auth
def logout(request):
auth.logout(request)
# Redirect to a success page.
return HttpResponseRedirect("/account/loggedout/")
La llamada a logout()
no produce ningún error, aun si no hubiera
ningún usuario conectado.
En la práctica, no es normalmente necesario escribir tus propias funciones para realizar estas tareas; el sistema de autentificación viene con un conjunto de vistas predefinidas para ello.
El primer paso para utilizar las vistas de autentificación es mapearlas en tu URLconf. Necesitas modificar tu código hasta tener algo parecido a esto:
from django.contrib.auth.views import login, logout
urlpatterns = patterns('',
# existing patterns here...
(r'^accounts/login/$', login),
(r'^accounts/logout/$', logout),
)
/accounts/login/
y /accounts/logout/
son las URL por defecto
que usa Django para estas vistas.
Por defecto, la vista de login
utiliza la plantilla definida en
registration/login.html
(puedes cambiar el nombre de la plantilla
utilizando un parámetro opcional, template_name
). El formulario
necesita contener un campo llamado username
y otro llamado
password
. Una plantilla de ejemplo podría ser esta:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p class="error">Sorry, that's not a valid username or password</p>
{% endif %}
<form action='.' method='post'>
<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next|escape }}" />
<form action='.' method='post'>
{% endblock %}
Si el usuario se identifica correctamente, su navegador será redirigido
a /accounts/profile/
. Puedes indicar una dirección distinta especificando
un tercer campo (normalmente oculto) que se llame next
, cuyo valor
debe ser la URL a redireccionar después de la identificación. También puedes
pasar este valor como un parámetro GET
a la vista de identificación
y se añadirá automáticamente su valor al contexto en una variable
llamada next
, que puedes incluir ahora en un campo oculto.
La vista de cierre de sesión se comporta de forma un poco diferente. Por
defecto utiliza la plantilla definida en registration/logged_out.html
(que
normalmente contiene un mensaje del tipo "Ha cerrado su sesión"). No obstante,
se puede llamar a esta vista con un parámetro extra, llamado next_page
, que
indicaría la vista a la que se debe redirigir una vez efectuado el cierre de
la sesión.
12.4.2. Limitar el acceso a los usuarios identificados
Por supuesto, la razón de haber implementado todo este sistema es permitirnos limitar el acceso a determinadas partes de nuestro sitio.
La forma más simple y directa de limitar este acceso es comprobar el resultado
de llamar a la función request.user.is_authenticated()
y redirigir
a una página de identificación, si procede:
from django.http import HttpResponseRedirect
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
# ...
O quizás mostrar un mensaje de error:
def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
# ...
Si se desea abreviar, se puede usar el decorador login_required
sobre las vistas que nos interese proteger:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
# ...
Esto es lo que hace el decorador login_required
:
- Si el usuario no está identificado, redirige a la
dirección
/accounts/login/
, incluyendo la url actual como un parámetro con el nombrenext
, por ejemplo/accounts/login/?next=/polls/3/
. - Si el usuario está identificado, ejecuta la vista sin ningún cambio. La vista puede asumir sin problemas que el usuario esta identificado correctamente
12.4.3. Limitar el acceso a usuarios que pasan una prueba
Se puede limitar el acceso basándose en ciertos permisos o en algún otro tipo de prueba, o proporcionar una página de identificación distinta de la vista por defecto, y las dos cosas se hacen de manera similar.
La forma más cruda es ejecutar las pruebas que queremos hacer directamente
en el código de la vista. Por ejemplo, para comprobar que el usuario está
identificado y que, además, tenga asignado el permiso polls.can_vote
(se explicará esto de los permisos con más detalle dentro de poco )
haríamos:
def vote(request):
if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')):
# vote here
else:
return HttpResponse("You can't vote in this poll.")
De nuevo, Django proporciona una forma abreviada llamada
user_passes_test
. Requiere que se la pasen unos argumentos
y genera un decorador especializado para cada situación en
particular:
def user_can_vote(user):
return user.is_authenticated() and user.has_perm("polls.can_vote")
@user_passes_test(user_can_vote, login_url="/login/")
def vote(request):
# Code here can assume a logged-in user with the correct permission.
...
El decorador user_passes_test
tiene un parámetro obligatorio: un
objeto que se pueda llamar (normalmente una función) y que a su vez
acepte como parámetro un objeto del tipo User
, y devuelva True
si el usuario puede acceder y False
en caso contrario. Es importante
destacar que user_passes_test
no comprueba automáticamente que el
usuario esté identificado; esa es una comprobación que se debe hacer
explícitamente.
En este ejemplo, hemos usado también un segundo parámetro opcional,
login_url
, que te permite indicar la url de la página que el
usuario debe utilizar para identificarse (/accounts/login/
por defecto).
Comprobar si un usuario posee un determinado permiso es una tarea
muy frecuente, así que Django proporciona una forma abreviada
para estos casos: El decorador permission_required()
. Usando
este decorador, el ejemplo anterior se podría codificar así:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote', login_url="/login/")
def vote(request):
# ...
El decorador permission_required()
también acepta el parámetro
opcional login_url
, de nuevo con el valor /accounts/login/
en caso de omisión.
Advertencia Una de las preguntas más frecuentes en la lista de usuarios de Django trata de cómo limitar el acceso a una vista genérica. Para conseguirlo, tienes que usar un recubrimiento sencillo alrededor de la vista que quieres proteger, y apuntar en tu URLconf al recubrimiento en vez de a la vista genérica:
from dango.contrib.auth.decorators import login_required
from django.views.generic.list_detail import object_detail
@login_required
def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs)
Puedes cambiar el decorador login_required
por cualquier otro
que quieras usar, como es lógico.
12.4.4. Gestionar usuarios, permisos y grupos
La forma más fácil de gestionar el sistema de autentificación es a través
de la interfaz de administración admin
. El Capítulo 6 describe como
usar esta interfaz para modificar los datos de los usuarios y controlar
sus permisos y accesos, y la mayor parte del tiempo esa es la forma
más adecuada de gestión.
A veces, no obstante, hace falta un mayor control, y para eso podemos utilizar las llamadas a bajo nivel que describiremos en este capítulo.
12.4.4.1. Crear usuarios
Puedes crear usuarios con el método create_user
:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user(username='john',
... email='[email protected]',
... password='glass onion')
En este momento, user
es una instancia de la clase User
, preparada
para ser almacenada en la base de datos (create_user()
no llama al
método save()
). Este te permite cambiar algunos de sus atributos
antes de guardarlos, si quieres:
>>> user.is_staff = True
>>> user.save()
12.4.4.2. Cambia contraseñas
Puedes cambiar las contraseña de un usuario llamando a set_password()
:
>>> user = User.objects.get(username='john')
>>> user.set_password('goo goo goo joob')
>>> user.save()
No debes modificar directamente el atributo password
, a no ser que
tengas muy claro lo que estás haciendo. La contraseña se almacena en
la base de datos en forma de código de comprobación (salted hash) y, por
tanto, debe ser modificada sólo a través de este método.
Para ser más exactos, el atributo password
de un objeto User
es una
cadena de texto con el siguiente formato:
hashtype$salt$hash
Es decir, el tipo de hash, el grano de sal (salt) y el código hash propiamente dicho, separados entre si por el carácter dolar ($).
El valor de hashtype
puede ser sha1
(por defecto) o md5
, el
algoritmo usado para realizar una transformación hash de un solo sentido
sobre la contraseña. El grano de sal es una cadena de texto
aleatoria que se utiliza para aumentar la resistencia de esta codificación
frente a un ataque por diccionario. Por ejemplo:
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
Las funciones User.set_password()
y User.check_password()
manejan
todos estos detalles y comprobaciones de forma transparente.
12.4.4.3. El alta del usuario
Podemos usar estas herramientas de bajo nivel para crear vistas que permitan al usuario darse de alta. Prácticamente todos los desarrolladores quieren implementar el alta del usuario a su manera, por lo que Django da la opción de crearte tu propia vista para ello. Afortunadamente, es muy fácil de hacer.
La forma más sencilla es escribir una pequeña vista que pregunte al usuario los datos que necesita y con ellos se cree directamente el usuario. Django proporciona un formulario prefabricado que se puede usar con este fin, como se muestra en el siguiente ejemplo:
from django import oldforms as forms
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.contrib.auth.forms import UserCreationForm
def register(request):
form = UserCreationForm()
if request.method == 'POST':
data = request.POST.copy()
errors = form.get_validation_errors(data)
if not errors:
new_user = form.save(data)
return HttpResponseRedirect("/books/")
else:
data, errors = {}, {}
return render_to_response("registration/register.html", {
'form' : forms.FormWrapper(form, data, errors)
})
Este formulario asume que existe una plantilla llamada
registration/register.html
. esa plantilla podría
consistir en algo parecido a esto:
{% extends "base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
<h1>Create an account</h1>
<form action="." method="post">
{% if form.error_dict %}
<p class="error">Please correct the errors below.</p>
{% endif %}
{% if form.username.errors %}
{{ form.username.html_error_list }}
{% endif %}
<label for="id_username">Username:</label> {{ form.username }}
{% if form.password1.errors %}
{{ form.password1.html_error_list }}
{% endif %}
<label for="id_password1">Password: {{ form.password1 }}
{% if form.password2.errors %}
{{ form.password2.html_error_list }}
{% endif %}
<label for="id_password2">Password (again): {{ form.password2 }}
<input type="submit" value="Create the account" />
</label>
{% endblock %}
12.4.5. Usar información de autentificación en plantillas
El usuario actual, así como sus permisos, están disponiblesen el contexto de la plantilla cuando usas RequestContext
(véase Capítulo 10).
Nota Técnicamente hablando, estas variables están disponibles en el contexto
de la plantilla sólo si usas RequestContext
y en la configuración
está incluido el valor "django.core.context_processors.auth"
en
la opción TEMPLATE_CONTEXT_PROCESSORS
, que es el valor que viene
predefinido cuando se crea un proyecto. Como ya se comentó, véase
el Capítulo 10 para más información.
Cuando se usa RequestContext
, el usuario actual (ya sea una instancia de
User
o de AnonymousUser
) es accesible en la plantilla con el
nombre {{ user }}
:
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
Los permisos del usuario se almacenan en la variable {{ perms }}
. En
realidad, es una forma simplificada de acceder a un par de métodos sobre los
permisos que veremos en breve.
Hay dos formas de usar este objeto perms
. Puedes usar {{ perms.polls }}
para comprobar si un usuario tienen algún permiso para una determinada
aplicación, o se puede usar una forma más específica, como
{{ perms.polls.can_vote }}
, para comprobar si el usuario tiene concedido un
permiso en concreto.
Por lo tanto, se pueden usar estas comprobaciones en sentencias {% if %}
:
{% if perms.polls %}
<p>You have permission to do something in the polls app.</p>
{% if perms.polls.can_vote %}
<p>You can vote!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the polls app.</p>
{% endif %}