2010-01-19 9 views
19

Quiero poder establecer una opción en la configuración del usuario que los obligue a cambiar su contraseña el próximo inicio de sesión en la interfaz de administración. es posible? ¿Cómo sería implementar? Estoy usando el modelo de autenticación predeterminado en este momento pero no me opongo a modificarlo o cambiarlo. Gracias por cualquier ayuda.¿Es posible implementar una característica de tipo "cambiar contraseña en el siguiente inicio de sesión" en django admin?

Respuesta

1

de un hilo en el Django Users mailing list:

Esto no es lo ideal, pero debería funcionar (o pronta a alguien a proponer algo mejor).

añadir una tabla de uno a uno para el usuario, con un campo que contiene el contraseña inicial (cifrada, por supuesto, por lo que se ve como la contraseña en la tabla auth_user).

Cuando el usuario inicia sesión, haga que la página de inicio de sesión compruebe si las contraseñas coinciden. Si lo hacen, redirija a la página de cambio de contraseña en lugar de a la página de redirección normal .

+0

¿Eso impide que los usuarios simplemente navegar fuera de la página de cambio de contraseña y ir a otro lugar? (Sin tener que cambiar su contraseña) – bparker

26

Estoy en el proceso de hacerlo yo mismo. Necesita tres componentes: un perfil de usuario (si no está ya en uso en su sitio), un componente de middleware y una señal de pre_save.

Mi código para esto está en una aplicación llamada 'cuentas'.

# myproject/accounts/models.py 

from django.db import models 
from django.db.models import signals 
from django.contrib.auth.models import User 

class UserProfile(models.Model): 
    user = models.ForeignKey(User, unique=True) 
    force_password_change = models.BooleanField(default=False) 

def create_user_profile_signal(sender, instance, created, **kwargs): 
    if created: 
     UserProfile.objects.create(user=instance) 

def password_change_signal(sender, instance, **kwargs): 
    try: 
     user = User.objects.get(username=instance.username) 
     if not user.password == instance.password: 
      profile = user.get_profile() 
      profile.force_password_change = False 
      profile.save() 
    except User.DoesNotExist: 
     pass 

signals.pre_save.connect(password_change_signal, sender=User, dispatch_uid='accounts.models') 

signals.post_save.connect(create_user_profile_signal, sender=User, dispatch_uid='accounts.models') 

En primer lugar, creamos un perfil de usuario con una clave externa para el usuario. El booleano force_password_change, como su nombre lo describe, se establecerá en verdadero para un usuario siempre que desee forzarlos a cambiar su contraseña. Sin embargo, puedes hacer cualquier cosa aquí. En mi organización, también decidimos implementar un cambio obligatorio cada 90 días, por lo que también tengo un DateTimeField que almacena la última vez que un usuario cambió su contraseña. A continuación, configure eso en la señal pre_save, password_changed_signal.

En segundo lugar, tenemos el create_user_profile_signal. Esto se agrega principalmente solo por completitud. Si acaba de agregar perfiles de usuario a su proyecto, necesitará una señal de post_save que creará un perfil de usuario cada vez que se cree un usuario. Esto logra esa tarea.

En tercer lugar, tenemos el password_changed_signal. Esta es una señal pre_save porque en este punto del proceso, la fila actual en la tabla de Usuario no se ha actualizado. Por lo tanto, podemos acceder tanto a la contraseña anterior como a la nueva contraseña que se va a guardar. Si los dos no coinciden, eso significa que el usuario ha cambiado su contraseña, y luego podemos restablecer el force_password_change booleano. Este sería el punto, también donde se encargaría de cualquier otra cosa que haya agregado, como establecer el DateTimeField previamente mencionado.

Las dos últimas líneas conectan las dos funciones a sus señales apropiadas.

Si no lo ha hecho, también tendrá que añadir la siguiente línea de su proyecto settings.py (cambiando la etiqueta de la aplicación y el nombre del modelo para que coincida con la configuración):

AUTH_PROFILE_MODULE = 'accounts.UserProfile' 

que cubre los conceptos básicos. Ahora necesitamos un componente de middleware para verificar el estado de nuestra bandera force_password_change (y cualquier otra verificación necesaria).

# myproject/accounts/middleware.py 

from django.http import HttpResponseRedirect 

import re 

class PasswordChangeMiddleware: 
    def process_request(self, request): 
     if request.user.is_authenticated() and \ 
      re.match(r'^/admin/?', request.path) and \ 
      not re.match(r'^/admin/password_change/?', request.path): 

      profile = request.user.get_profile() 
      if profile.force_password_change: 
       return HttpResponseRedirect('/admin/password_change/') 

esta simple ganchos de middleware en la etapa process_request del proceso de carga de la página. Comprueba que 1) el usuario ya ha iniciado sesión, 2) está intentando acceder a una página en el administrador, y 3) que la página a la que accede no es la misma página de cambio de contraseña (de lo contrario, obtendría un bucle infinito de redirecciones). Si todo esto es cierto y el indicador force_password_change se configuró en True, se redirige al usuario a la página de cambio de contraseña. No podrán navegar en ningún otro lado hasta que cambien su contraseña (disparando la señal pre_save discutida anteriormente).

Por último, sólo tiene que añadir este middleware de su proyecto settings.py (de nuevo, cambiando la ruta de importación, según sea necesario):

MIDDLEWARE_CLASSES = (
    # Other middleware here 
    'myproject.accounts.middleware.PasswordChangeMiddleware', 
) 
+0

En realidad, mi único problema en este momento está alterando la página de cambio de contraseña del administrador para que pueda mostrar un mensaje al usuario para explicar por qué terminaron aquí. Simplemente poner una plantilla de anulación en el directorio de plantillas de su proyecto no funciona (aparentemente se ignora por completo). Y establecer 'admin.site.password_change_template' (nuevo en 1.2) en la ruta a su plantilla de modificación funciona solo en parte. La plantilla se modifica lo suficiente, pero parece perder el contexto de visualización (faltan todos los campos de formulario, etc.). Si alguien puede ayudar con eso, por favor meter su cuchara. –

+0

parece una solución bastante completa – cerberos

9

he utilizado solución de Chris Pratt, con un pequeño cambio: en lugar de utilizar un middleware, que se ejecutaría para cada página con el consiguiente uso de recursos, pensé que simplemente interceptaría la vista de inicio de sesión.

En mi urls.py he añadido esto a mis urlpatterns:

url(r'^accounts/login/$', 'userbase.views.force_pwd_login'), 

Luego añade lo siguiente a userbase.views:

def force_pwd_login(request, *args, **kwargs): 
    response = auth_views.login(request, *args, **kwargs) 
    if response.status_code == 302: 
     #We have a user 
     try: 
      if request.user.get_profile().force_password_change: 
       return redirect('django.contrib.auth.views.password_change') 
     except AttributeError: #No profile? 
      pass 
    return response 

Parece que funciona sin problemas en Django 1.2, pero no tengo ninguna razón para creer que 1.3+ debería tener problemas con eso.

0

Verifique este paquete simple basado en la sesión (Probado con django 1.8). https://github.com/abdullatheef/django_force_reset_password

Crear vista personalizada en myapp.views.py

class PassWordReset(admin.AdminSite): 

    def login(self, request, extra_context=None): 
     if request.method == 'POST': 
      response = super(PassWordReset, self).login(request, extra_context=extra_context) 
      if response.status_code == 302 and request.user.is_authenticated(): 
       if not "fpr" in request.session or request.session['fpr']: 
        request.session['fpr'] = True 
        return HttpResponseRedirect("/admin/password_change/") 
      return response 
     return super(PassWordReset, self).login(request, extra_context=extra_context) 

    def password_change(self, request, extra_context=None): 
     if request.method == 'POST': 
      response = super(PassWordReset, self).password_change(request, extra_context=extra_context) 
      if response.status_code == 302 and request.user.is_authenticated(): 
       request.session['fpr'] = False 
      return response 
     return super(PassWordReset, self).password_change(request, extra_context=extra_context) 


pfr_login = PassWordReset().login 
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True) 

Luego, en proyecto/urls.py

from myapp.views import pfr_password_change, pfr_login 
urlpatterns = [ 
    ...... 
    url(r'^admin/login/$', pfr_login), 
    url(r'^admin/password_change/$', pfr_password_change), 
    url(r'^admin/', admin.site.urls), 
    .... 
] 

A continuación, agregue este middleware miaplicacion/middleware.py

class FPRCheck(object): 
    def process_request(self, request): 
     if request.user.is_authenticated() \ 
       and re.match(r'^/admin/?', request.path) \ 
       and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \ 
       and not re.match(r"/admin/password_change|/admin/logout", request.path): 
      return HttpResponseRedirect("/admin/password_change/") 

Orden de middleware

MIDDLEWARE_CLASSES = [ 
    .... 

    'myapp.middleware.FPRCheck' 
    ] 

Nota

  • Esto no tendrá ningún modelo extra.
  • También funciona con cualquier motor de sesión.
  • No hay consultas db dentro de middleware.
+1

@EBH actualizado la respuesta – itzMEonTV

0

Este es el middleware que uso con Django 1.11:

# myproject/accounts/middleware.py 

from django.http import HttpResponseRedirect 
from django.urls import reverse 


class PasswordChangeMiddleware: 
    def __init__(self, get_response): 
     self.get_response = get_response 

    def __call__(self, request): 
     response = self.get_response(request) 
     next = reverse('client:password-update') 
     if request.user.is_authenticated() and request.path != next: 
      if request.user.account.force_password_change: 
       return HttpResponseRedirect(next) 

     return response 

embargo de añadir a la lista de middleware ajustes:

MIDDLEWARE_CLASSES = (
    # Other middleware here 
    'myproject.accounts.middleware.PasswordChangeMiddleware', 
) 
Cuestiones relacionadas