El libro de Django 1.0

16.2. Integración con un sistema de autentificación

Es posible integrar Django con un sistema de autentificación existente — otra fuente de nombres de usuario y contraseñas o métodos de autentificación.

Por ejemplo, tu compañía ya puede tener una configuración LDAP que almacena un nombre de usuario y contraseña para cada empleado. Sería una molestia tanto para el administrador de red como para los usuarios, si cada uno de ellos tiene cuentas separadas en LDAP y en las aplicaciones basadas en Django.

Para manejar situaciones como ésta, el sistema de autentificación de Django te permite conectarte con otras fuentes de autentificación. Puedes anular el esquema por omisión de Django basado en base de datos, o puedes usar el sistema por omisión en conjunto con otros sistemas.

16.2.1. Especificar los back-ends de autentificación

Detrás de escena, Django mantiene una lista de "back-ends de autentificación" que utiliza para autentificar. Cuando alguien llama a django.contrib.auth.authenticate() (como se describió en el Capítulo 12), Django intenta autentificar usando todos sus back-ends de autentificación. Si el primer método de autentificación falla, Django intenta con el segundo, y así sucesivamente, hasta que todos los back-ends han sido intentados.

La lista de back-ends de autentificación a usar se especifica en la configuración AUTHENTICATION_BACKENDS. Ésta debe ser una tupla de nombres de ruta Python que apuntan a clases que saben cómo autentificar. Estas clases pueden estar en cualquier lugar de tu ruta Python.

Por omisión, AUTHENTICATION_BACKENDS contiene lo siguiente:

('django.contrib.auth.backends.ModelBackend',)

Ese es el esquema básico de autentificación que verifica la base de datos de usuarios de Django.

El orden de AUTHENTICATION_BACKENDS se tiene en cuenta, por lo que si el mismo usuario y contraseña son válidos en múltiples back-ends, Django detendrá el procesamiento en la primera coincidencia positiva.

16.2.2. Escribir un back-end de autentificación

Un back-end de autentificación es un clase que implementa dos métodos: get_user(id) y authenticate(**credentials).

El método get_user recibe un id — el cual podría ser un nombre de usuario, un ID de la base de datos o cualquier cosa — y devuelve un objeto User.

El método authenticate recibe credenciales como argumentos de palabras clave. La mayoría de las veces se parece a esto:

class MyBackend(object):
    def authenticate(self, username=None, password=None):
        # Check the username/password and return a User.

Pero podría tambien autentificar un token, como se muestra a continuación:

class MyBackend(object):
    def authenticate(self, token=None):
        # Check the token and return a User.

De cualquier manera, authenticate debe verificar las credenciales que recibe, y debe retornar un objeto User que coincide con esas credenciales, si las credenciales son válidas. Si no son válidas, debe retornar None.

El sistema de administración de Django esta altamente acoplado a su propio objeto User respaldado por base de datos descrito en el Capítulo 12. La mejor manera de lidiar con esto es crear un objeto User de Django para cada usuario que existe en tu back-end (ej., en tu directorio LDAP, tu base de datos SQL externa, etc.). De cualquier manera puedes escribir un script para hacer esto por adelantado o tu método de autentificación puede hacerlo la primera vez que el usuario ingresa al sistema.

Aquí está un ejemplo de back-end que autentifica contra unas variables de usuario y contraseña definidas en tu archivo settings.py y crea un objeto User de Django la primera vez que un usuario se autentifica:

from django.conf import settings
from django.contrib.auth.models import User, check_password

class SettingsBackend(object):
    """
    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