2011-01-27 19 views
17

Tengo el siguiente formulario. ¿Cómo puedo volver a verificar la contraseña del usuario antes de que el usuario pueda cambiar finalmente su emailadsress? Incluso él está conectado, solo quiero estar seguro de que realmente es el usuario. Solo una cosa de seguridad.check_password() de un usuario nuevo

¿Cómo lo hago con .check_password()?

'EmailChangeForm' object has no attribute 'user' 

    /home/craphunter/workspace/project/trunk/project/auth/user/email_change/forms.py in clean_password, line 43 

from django import forms 
from django.db.models.loading import cache 
from django.utils.translation import ugettext_lazy as _ 
from django.contrib.auth.models import User 


class EmailChangeForm(forms.Form): 

    email = forms.EmailField(label='New E-mail', max_length=75) 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, user, *args, **kwargs): 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 
     self.user = user 

    def clean_password(self): 
     valid = self.user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 
     return valid 

    def __init__(self, username=None, *args, **kwargs): 
     """Constructor. 

     **Mandatory arguments** 

     ``username`` 
      The username of the user that requested the email change. 

     """ 
     self.username = username 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_email(self): 
     """Checks whether the new email address differs from the user's current 
     email address. 

     """ 
     email = self.cleaned_data.get('email') 

     User = cache.get_model('auth', 'User') 
     user = User.objects.get(username__exact=self.username) 

     # Check if the new email address differs from the current email address. 
     if user.email == email: 
      raise forms.ValidationError('New email address cannot be the same \ 
       as your current email address') 

     return email 

Respuesta

17

Me refactorizar su código a ser algo como esto:

Vista:

@login_required 
def view(request, extra_context=None, ...): 

    form = EmailChangeForm(user=request.user, data=request.POST or None) 

    if request.POST and form.is_valid(): 
     send_email_change_request(request.user, 
            form.cleaned_data['email'], 
            https=request.is_secure()) 
     return redirect(success_url) 
    ... 

validación de contraseña va a la forma: la lógica

class EmailChangeForm(Form): 
    email = ... 
    old_password = CharField(..., widget=Password()) 

    def __init__(self, user, data=None): 
     self.user = user 
     super(EmailChangeForm, self).__init__(data=data) 

    def clean_old_password(self): 
     password = self.cleaned_data.get('password', None) 
     if not self.user.check_password(password): 
      raise ValidationError('Invalid password') 

Extracto de vista:

def send_email_change_request(user, new_email, https=True): 

    site = cache.get_model('sites', 'Site') 

    email = new_email 
    verification_key = generate_key(user, email) 

    current_site = Site.objects.get_current() 
    site_name = current_site.name 
    domain = current_site.domain 

    protocol = 'https' if https else 'http' 

    # First clean all email change requests made by this user 
    qs = EmailChangeRequest.objects.filter(user=request.user) 
    qs.delete() 

    # Create an email change request 
    change_request = EmailChangeRequest(
     user = request.user, 
     verification_key = verification_key, 
     email = email 
    ) 
    change_request.save() 

    # Prepare context 
    c = { 
     'email': email, 
     'site_domain': 'dev.tolisto.de', 
     'site_name': 'tolisto', 
     'user': self.user, 
     'verification_key': verification_key, 
     'protocol': protocol, 
    } 
    c.update(extra_context) 
    context = Context(c) 

    # Send success email 
    subject = "Subject" # I don't think that using template for 
         # subject is good idea 
    message = render_to_string(email_message_template_name, context_instance=context) 

    send_mail(subject, message, None, [email]) 

No ponga cosas complicadas dentro de vistas (como renderizar y enviar correos electrónicos).

+0

Muchas gracias. ¡Ayudó también! – craphunter

9

siento que responde a su propia pregunta:)

La documentación sobre el método check_password están aquí: http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.check_password

success = user.check_password(request.POST['submitted_password']) 
if success: 
    # do your email changing magic 
else: 
    return http.HttpResponse("Your password is incorrect") 
    # or more appropriately your template with errors 

Dado que ya está pasando en request.user en su constructor de formulario (parece que ha anulado __init__ por sus propios motivos) puede poner toda su lógica en el formulario sin ningún problema.

class MyForm(forms.Form): 
    # ... 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, user, *args, **kwargs): 
      super(MyForm, self).__init__(*args, **kwargs) 
      self.user = user 

    def clean_password(self): 
     valid = self.user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 
     return valid 

actualización después de ver sus formas

OK. El principal problema es que __init__ se ha definido dos veces, por lo que la primera declaración es inútil. El segundo problema que veo es que estaríamos haciendo múltiples consultas para user cuando realmente no es necesario.

Nos hemos desviado bastante de su pregunta original, pero espero que esta sea una experiencia de aprendizaje.

he cambiado sólo unas pocas cosas:

  • ha eliminado el __init__ definición adicional
  • Cambiado __init__ a aceptar una instancia User en lugar de un texto username
  • quitado la consulta para User.objects.get(username=username) ya que' re pasando en un objeto de usuario.

Sólo recuerda que pasar constructor del formulario user=request.user en lugar de username=request.user.username

class EmailChangeForm(forms.Form): 
    email = forms.EmailField(label='New E-mail', max_length=75) 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, user=None, *args, **kwargs): 
     self.user = user 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_password(self): 
     valid = self.user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 

    def clean_email(self): 
     email = self.cleaned_data.get('email') 

     # no need to query a user object if we're passing it in anyways. 
     user = self.user 

     # Check if the new email address differs from the current email address. 
     if user.email == email: 
      raise forms.ValidationError('New email address cannot be the same \ 
       as your current email address') 

     return email 

Por último, ya que estamos hablando de buenas prácticas aquí, te recomiendo seguir adelante con Skirmantas sugerencias acerca de cómo mover su código de vista actual a un método de formulario para que simplemente llame al myform.send_confirmation_email.

¡Suena como un buen ejercicio!

+0

Gracias amigo! Sí, soy un recién nacido en django ... a veces la solución es tan simple. – craphunter

+0

No hay problema, hombre! Sí, me encantan django y python. Ah, ¿sabes qué? Para simplificar, se me ocurre que debe agregar un campo de contraseña a su formulario ya que está pasando el objeto del usuario. Simplemente defina 'password = forms.CharField (widget = forms.PasswordInput})' y anule 'clean_password' para hacer la verificación de la contraseña. De esa forma, puede permanecer en el marco de formularios para volver a mostrar el formulario de error. –

+0

¿Qué quieres decir con clean_password? ¿Hay un error de tipeo en la contraseña})? – craphunter

1

gracias de nuevo a Yuji. Funciona cuando no tengo en mi primer el usuario variable. También agregué en def clean_password las primeras 2 líneas de def clean_email

from django import forms 
from django.db.models.loading import cache 
from django.utils.translation import ugettext_lazy as _ 
from django.contrib.auth.models import User 


class EmailChangeForm(forms.Form): 

    email = forms.EmailField(label='New E-mail', max_length=75) 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, *args, **kwargs): 
     self.user = user 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_password(self): 
     User = cache.get_model('auth', 'User') 
     user = User.objects.get(username__exact=self.username) 
     valid = user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 
     return valid 

    def __init__(self, username=None, *args, **kwargs): 
     """Constructor. 

     **Mandatory arguments** 

     ``username`` 
      The username of the user that requested the email change. 

     """ 
     self.username = username 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_email(self): 
     """Checks whether the new email address differs from the user's current 
     email address. 

     """ 
     email = self.cleaned_data.get('email') 

     User = cache.get_model('auth', 'User') 
     user = User.objects.get(username__exact=self.username) 

     # Check if the new email address differs from the current email address. 
     if user.email == email: 
      raise forms.ValidationError('New email address cannot be the same \ 
       as your current email address') 

     return email 
+0

¡Felicitaciones por que esto funciona! Sin embargo, eche un vistazo a mi respuesta actualizada ya que está haciendo 2 consultas para el usuario cuando podría ser ninguna. Podría ser un buen ejercicio :) –

+0

Gracias de nuevo a Portland. ¡Hice tu consejo y está funcionando! ¡Me voy a la cama! ¡En Alemania son las 2 de la mañana! ¡Que tengas una buena noche! – craphunter

Cuestiones relacionadas