2009-09-10 22 views
23

Estoy construyendo un marco de sitio de Django que alimentará varios sitios independientes, todos usando las mismas aplicaciones pero con sus propias plantillas. Planeo lograr esto usando varios archivos de configuración y estableciendo un SITE_ID único para ellos, como sugerido en los documentos de Django para el django.contrib.sites framework¿Cómo obtener usuarios únicos en múltiples sitios de Django que funcionan con el marco de "sitios"?

Sin embargo, no quiero que un usuario del sitio A pueda iniciar sesión en sitio B. Después de inspeccionar la tabla de usuarios creada por syncdb, no veo ninguna columna que pueda restringir a un usuario a un sitio específico. También traté de crear un usuario, 'bob' en un sitio y luego usar el comando de shell para listar a todos los usuarios del otro lado, y efectivamente, bob aparece allí.

¿Cómo puedo garantizar que todos los usuarios estén restringidos a sus sitios respectivos?

+0

Gran pregunta - No sé si es posible, pero tal vez alguien más lo sepa mejor. –

Respuesta

25

La forma más compatible de hacer esto sería crear un user Profile model que incluye una clave externa para el modelo de sitio, luego escribir un custom auth backend que verifique el sitio actual con el valor de ese FK. Un código de ejemplo:

Definir el modelo de su perfil, digamos que en app/models.py:

from django.db import models 
from django.contrib.sites.models import Site 
from django.contrib.auth.models import User 

class UserProfile(models.Model): 
    user = models.OneToOneField(User) 
    site = models.ForeignKey(Site) 

Escribe tu backend de autenticación personalizada, heredando de la que viene por defecto, digamos que en app/auth_backend.py:

from django.contrib.auth.backends import ModelBackend 
from django.contrib.sites.models import Site 

class SiteBackend(ModelBackend): 
    def authenticate(self, **credentials): 
     user_or_none = super(SiteBackend, self).authenticate(**credentials) 
     if user_or_none and user_or_none.userprofile.site != Site.objects.get_current(): 
      user_or_none = None 
     return user_or_none 

    def get_user(self, user_id): 
     try: 
      return User.objects.get(
       pk=user_id, userprofile__site=Site.objects.get_current()) 
     except User.DoesNotExist: 
      return None 

Este auth backend asume que todos los usuarios tienen un perfil; deberías asegurarte de que tu proceso de creación/registro de usuario siempre crea uno.

El método authenticate reemplazado asegura que un usuario solo pueda iniciar sesión en el sitio correcto.Se llama al método get_user en cada solicitud para captar al usuario de la base de datos en función de la información de autenticación almacenada en la sesión del usuario; nuestra anulación garantiza que un usuario no pueda iniciar sesión en el sitio A y luego use esa misma cookie de sesión para obtener acceso no autorizado al sitio B. (Gracias a Jan Wrobel por señalar la necesidad de manejar este último caso)

+1

Gracias Carl, voy a utilizar su implementación y espero aprender algo sobre backends custon en el proceso. – westmark

+4

Noe que este backend solo hace una parte del trabajo. No permite al usuario de un sitio iniciar sesión en el otro sitio, lo que no es suficiente. Un usuario puede iniciar sesión en el sitio A y copiar manualmente la cookie de autenticación para enviarla al sitio B. Dicha cookie será reconocida como válida y el usuario iniciará sesión (el backend de autenticación se invoca solo cuando no hay una cookie de sesión válida)) Para completar la solución, necesita un middleware que se invocará para cada solicitud a sus sitios y que verificará que el usuario haya iniciado sesión en el sitio con el que está relacionada la solicitud, no en otro sitio. –

+3

@JanWrobel Creo que en lugar de un middleware por separado, puede implementar una comprobación similar en el método 'get_user' de su servidor de autenticación. Este método se invoca en cada solicitud, no solo en el momento de inicio de sesión. Actualizaré la respuesta para incluir esto; gracias por señalar la omisión. –

2

Puede conectar su propia autorización y back-end de autenticación que tengan en cuenta la identificación del sitio.

Ver other authentication sources en la documentación de Django y la authentication backends references

Además de eso, si la fuente de Django es demasiado viejo, siempre se puede modificar la autenticación (pase) o inicia sesión() código usted mismo. Después de todo ... ¿No es esa una de las maravillas del código abierto? Tenga en cuenta que al hacerlo puede afectar su compatibilidad con otros módulos.

Espero que esto ayude.

+0

Escribir un backend personalizado es ciertamente algo que he considerado, pero rechazado por el momento ya que no creo que tenga las habilidades necesarias. Lo mismo ocurre con la modificación del código real de Django. – westmark

+0

Escribir un auth back-end personalizado no es difícil, probablemente se trata de seis líneas de código. Una de las cosas más fáciles de hacer en Django. Para mantener la máxima compatibilidad, coloque una ForeignKey en el sitio en el modelo de perfil de usuario y desactívela en su servidor de autenticación personalizado. –

+0

Rechazaría esta respuesta si no recomendara modificar directamente la fuente de Django. No hay absolutamente ninguna razón para hacer eso cuando los back-end de autenticación personalizados están disponibles, ni siquiera deberían mencionarse en la respuesta. –

-1

Tienes que saber que muchas personas se quejan por los privilegios y el sistema de autorización predeterminado de Django; simplemente tiene reglas para los objetos, para las instancias de los objetos, lo que significa que sin escribir ningún código no sería posible.

Sin embargo, hay algunos ganchos de autorización que puede le ayuda a lograr este objetivo, por ejemplo:

Tome un vistazo aquí: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py y de Permiso clase.

Puede agregar su propio permiso y definir reglas para ellos (hay una ForeignKey para el usuario y para ContentType).

Sin embargo, sin monkeypatching/cambiar algunos métodos podría ser difícil.

Cuestiones relacionadas