2010-01-19 15 views
19

Desidero poter impostare un'opzione nelle impostazioni dell'utente che impone loro di cambiare la password al successivo accesso all'interfaccia di amministrazione. È possibile? Come dovrebbe essere implementato? Sto usando il modello di autenticazione predefinito in questo momento, ma non contrario a modificarlo o modificarlo. Grazie per qualsiasi aiuto.È possibile implementare una funzionalità di tipo "modifica password all'accesso successivo" nell'amministratore di django?

risposta

1

Da un thread sul Django Users mailing list:

Questo non è l'ideale, ma dovrebbe funzionare (o prompt di qualcuno a proporre qualcosa di meglio).

aggiungere una tabella one-to-one per l'utente, con un campo che contiene la password iniziale (criptato, ovviamente, in modo che sembra la password nella tabella auth_user).

Quando l'utente esegue il login, verifica la pagina di accesso per verificare se le password corrispondono a . In tal caso, reindirizzare alla pagina di modifica della password anziché alla pagina di reindirizzamento normale .

+0

Volesse che impedisce agli utenti di navigare semplicemente uscire dalla pagina di modifica della password e andare da qualche altra parte? (senza mai cambiare la password) – bparker

26

Sono in procinto di farlo da solo. Sono necessari tre componenti: un profilo utente (se non già in uso sul tuo sito), un componente middleware e un segnale pre_save.

Il mio codice per questo è in un'app denominata "account".

# 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') 

Innanzitutto, creiamo un profilo utente con una chiave esterna per l'utente. Il valore booleano force_password_change, come viene descritto nel nome, verrà impostato su true per un utente ogni volta che si desidera forzare la modifica della password. Potresti fare qualsiasi cosa qui però. Nella mia organizzazione, abbiamo anche scelto di implementare una modifica obbligatoria ogni 90 giorni, quindi ho anche un DateTimeField che memorizza l'ultima volta che un utente ha cambiato la password. Quindi lo hai impostato nel segnale pre_save, password_changed_signal.

In secondo luogo, abbiamo il create_user_profile_signal. Questo è per lo più aggiunto solo per completezza. Se stai solo aggiungendo i profili utente al tuo progetto, avrai bisogno di un segnale post_save che crei un profilo utente ogni volta che viene creato un utente. Questo ha portato a termine questo compito.

In terzo luogo, abbiamo il password_changed_signal. Questo è un segnale pre_save perché a questo punto del processo la riga effettiva nella tabella Utente non è stata aggiornata. Pertanto, possiamo accedere sia alla password precedente che alla nuova password che sta per essere salvata. Se i due non corrispondono, significa che l'utente ha cambiato la propria password e possiamo quindi ripristinare il valore booleano force_password_change. Questo sarebbe il punto, anche dove ti occuperesti di tutte le altre cose che hai aggiunto come l'impostazione di DateTimeField precedentemente menzionate.

Le ultime due righe collegano le due funzioni ai segnali appropriati.

Se non l'hai già, si dovrà anche aggiungere la seguente riga a del progetto settings.py (cambiando l'etichetta app e il nome del modello per abbinare il vostro setup):

AUTH_PROFILE_MODULE = 'accounts.UserProfile' 

che copre le nozioni di base. Ora abbiamo bisogno di un componente middleware per verificare lo stato del nostro flag force_password_change (e qualsiasi altro controllo necessario).

# 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/') 

This molto semplici ganci middleware nella process_request fase del processo di caricamento della pagina. Controlla che 1) l'utente abbia già effettuato l'accesso, 2) stiano tentando di accedere ad alcune pagine nell'amministratore e 3) la pagina a cui stanno accedendo non sia la stessa pagina di modifica della password (altrimenti si otterrebbe un ciclo infinito di reindirizzamenti). Se tutti questi sono veri e il flag force_password_change è stato impostato su True, l'utente viene reindirizzato alla pagina di modifica della password. Non saranno in grado di navigare da nessun'altra parte finché non cambiano la loro password (attivando il segnale pre_save discusso in precedenza).

Infine, non vi resta che aggiungere questo middleware del progetto settings.py (ancora una volta, la modifica del percorso di importazione, se necessario):

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

In realtà, il mio unico problema al momento è modificare la pagina della password di modifica dell'amministratore in modo da poter visualizzare un messaggio all'utente spiegando perché sono finiti qui. Il semplice inserimento di un modello di sostituzione nella directory dei modelli del progetto non funziona (apparentemente è completamente ignorato). E l'impostazione di 'admin.site.password_change_template' (nuova in 1.2) sul percorso del tuo modello di override funziona solo in parte. Il modello è stato modificato abbastanza bene, ma sembra perdere il contesto della vista (mancano tutti i campi modulo, ecc.). Se qualcuno può aiutare con questo, per favore chime. –

+0

Sembra una soluzione piuttosto completa – cerberos

9

Ho usato la soluzione di Chris Pratt, con un piccolo cambiamento: invece di utilizzare un middleware, che sarebbe stato eseguito per ogni pagina con il conseguente utilizzo delle risorse, ho pensato che avrei solo intercettato la vista di login.

Nel mio urls.py Ho aggiunto questo ai miei urlpatterns:

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

poi ho aggiunto il testo seguente 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 

Sembra funzionare senza problemi su Django 1.2, ma non ho motivo di credere che 1.3+ dovrebbe avere problemi con esso.

0

Controlla questo semplice pacchetto basato sulla sessione (Testato con django 1.8). https://github.com/abdullatheef/django_force_reset_password

Crea visualizzazione personalizzata in 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) 

Poi nel progetto/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), 
    .... 
] 

Quindi aggiungere questo middleware frontend/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/") 

Ordine di middleware

MIDDLEWARE_CLASSES = [ 
    .... 

    'myapp.middleware.FPRCheck' 
    ] 

Nota

  • Questo non avrà bisogno di alcun modello extra.
  • Funziona anche con qualsiasi motore di sessione.
  • Nessuna query DB all'interno del middleware.
+1

@ EBH ha aggiornato la risposta – itzMEonTV

0

Questo è il middleware che 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 

Ancora aggiungendolo alla lista impostazioni middleware:

MIDDLEWARE_CLASSES = (
    # Other middleware here 
    'myproject.accounts.middleware.PasswordChangeMiddleware', 
) 
Problemi correlati