def forgot_password(request): """Form for requesting a PasswordResetToken for a specified account. Accepts email or username. Keeps for each user at most one token at a time in the database. Also, it saves FakeTokens to obfuscate an account's existence whilst at the same time forcing a 5 minutes gap between the sending of two tokens for the same account.""" if request.user.is_authenticated(): return not_logged_out_routine(request) if request.method == 'POST': form = ForgotPasswordForm(request.POST) if form.is_valid(): identifier = form.cleaned_data['identifier'] user = None # do we need this here? -- Probably. try: # Form input could be an email or a username if looks_like_email(identifier): user = CustomUser.objects.get(email=identifier) else: user = CustomUser.objects.get(username=identifier) # At this point, we do have a user object. try: # Between sending two tokens for the same user, a certain period should pass (--> settings) previous_token = PasswordResetToken.objects.get(user=user) if previous_token.blocks_new(): messages.error(request, 'You need to wait between sending two tokens for the same account.') return redirect('accounts:forgot_password') # Only one token per user in the database; and we are about to create a new one. else: previous_token.delete() except PasswordResetToken.DoesNotExist: # Fair enough. pass # Alright, create the new token and send it. token = PasswordResetToken(user=user) token.save() request.session['token_value'] = token.value # EXTEND: send email except CustomUser.DoesNotExist: # No such user, therefor no email or token. But we do want a FakeToken. # See models.FakeToken or the docstring here for explanation why we do this FakeToken thing. try: # Clear all previous FakeTokens. # No difference between email or username here: previous_token = FakeToken.objects.get(user_identifier=identifier) if previous_token.blocks_new() == True: messages.error(request, 'You need to wait between sending two tokens for the same account.') return redirect('accounts:forgot_password') else: previous_token.delete() except FakeToken.DoesNotExist: pass ft = FakeToken(user_identifier=identifier) ft.save() request.session['token_value'] = None # we want to tell the user whether he had stated a username or an # email address; and we want to tell him which of both he stated if looks_like_email(identifier): request.session['token_email'] = identifier request.session['token_username'] = None else: request.session['token_email'] = None request.session['token_username'] = identifier return redirect('accounts:token_sent') else: form = ForgotPasswordForm return render(request, 'accounts/forgot_password.html', { 'form':form })