1

I need to manually send a password reset email upen creating a new user. I followed the steps described here and created a drive-by HttpRequest (pwreset_request = HttpRequest()) to do so, but all I get in return to calling PasswordResetView.as_view()(pwreset_request) is a 403 because of an invalid CSRF token.

Despite that (and consequently getting that email) there are no errors on the page and everything is working fine.

from django.http import HttpResponseForbidden, HttpResponse, HttpRequest
from django.middleware.csrf import get_token

@login_required
@permission_required(["abonnenten.verlag_sachbearbeiter_fullaccess"])
def sachbearbeiter_create_or_edit(request, username=None):
    mode = "create" if username is None else "edit"

    if mode == "create":
        user = None
        sachbearbeiter = None
    else:
        user = get_object_or_404(User, username=username)
        sachbearbeiter = user.sachbearbeiter

        if user.is_superuser or user.is_staff:
            raise PermissionDenied

    user_form = UserForm(request.POST or None, instance=user)
    sachbearbeiter_form = SachbearbeiterForm(request.POST or None, instance=sachbearbeiter)

    if request.method == 'POST':
        if mode == "create":
            if user_form.is_valid() and sachbearbeiter_form.is_valid():
                user = user_form.save(commit=False)
                user.email = user.username
                user.save()
                sachbearbeiter = sachbearbeiter_form.save(commit=False)
                sachbearbeiter.user = user
                sachbearbeiter.save()

                # Manually send password reset mail
                # TODO Fix this shit (403)
                pwreset_request = HttpRequest()
                pwreset_request.method = 'POST'
                pwreset_request.META['HTTP_HOST'] = request.META['HTTP_HOST']
                pwreset_request.POST = {'email': user.email, 'csrfmiddlewaretoken': get_token(HttpRequest())}
                PasswordResetView.as_view()(pwreset_request)

                messages.success(request, 'Der Datensatz wurde erfolgreich gespeichert.')
                return redirect('sachbearbeiter_edit', username=user.username)
        elif mode == "edit":
            if user_form.is_valid() and sachbearbeiter_form.is_valid():
                user = user_form.save(commit=False)
                user.email = user.username
                user.save()
                sachbearbeiter_form.save()
                messages.success(request, 'Der Datensatz wurde erfolgreich gespeichert.')
                return redirect('sachbearbeiter_edit', username=user.username)
        else:
            raise ValueError("Variable `mode` must be 'create' or 'edit'")

    return render(request,
                  'sachbearbeiter/edit_or_create.html',
                  {'mode': mode,
                   'user': user,
                   'user_form': user_form,
                   'sachbearbeiter_form': sachbearbeiter_form})

Any ideas how to fix that?

Daniel
  • 3,092
  • 3
  • 32
  • 49
  • I don't think that helps anyone, but here you go. – Daniel Nov 14 '17 at 09:44
  • lol what do you need the template for? This has NOTHING to do with the template and I won't flood my question with any more useless clutter. – Daniel Nov 14 '17 at 09:47
  • Again, it's not a problem with the template or the view. It's a problem with the `pwreset_request = HttpRequest()` only. I already tried `get_token(HttpRequest())` to no avail. – Daniel Nov 14 '17 at 09:55

1 Answers1

2

Why creating by hand a new Request object, calling the password reset view, when you can just use the PasswordResetForm used by this view ?

So replace all your code below:

# Manually send password reset mail
# TODO Fix this shit (403)
pwreset_request = HttpRequest()
pwreset_request.method = 'POST'
pwreset_request.META['HTTP_HOST'] = request.META['HTTP_HOST']
pwreset_request.POST = {'email': user.email, 'csrfmiddlewaretoken': get_token(HttpRequest())}
PasswordResetView.as_view()(pwreset_request)

By this code:

from django.contrib.auth.forms import PasswordResetForm

reset_password_form = PasswordResetForm(data={'email': user.email})
if reset_password_form.is_valid():
     reset_password_form.save(request=request) # The save method will send the email
Daniel
  • 3,092
  • 3
  • 32
  • 49
Clément Denoix
  • 1,504
  • 11
  • 18
  • I still got an error on `reset_password_form.save()` because request was NoneType. Seems obvious since it relies on being passed a `request` instance for the site name. The fix is to pass the request as a parameter like so: `reset_password_form.save(request=request)`. Thanks. – Daniel Nov 14 '17 at 10:29
  • Sorry for this, I tested the code above directly in the `shell_plus` so maybe he don't need a request here. Accepted your edit. – Clément Denoix Nov 14 '17 at 10:33