def get(self): if self.request.get("token"): r = PasswordResetToken.get_by_id(self.request.get("token")) if r: user = User.get_by_id(r.email) self.tv["current_user"] = user.to_object() self.tv["token"] = self.request.get("token") self.render("frontend/reset-password.html") else: self.redirect('/password/reset?error='+ urllib.quote('Invalid Token.')) else: self.render("frontend/reset-password.html")
def post(self): if self.request.get("password_original") == self.request.get("password_retype"): if self.request.get("email"): user = User.get_by_id(self.request.get("email")) user.password = hash_password(self.request.get("email"), self.request.get("password_original")) user.put() self.login(user) r = PasswordResetToken.get_by_id(self.request.get("token")) r.key.delete() self.redirect(self.uri_for('www-dashboard')) else: self.redirect('/password/reset?error='+ urllib.quote('Password does not match!.')) return
def post(self): if self.request.get('email'): self.tv["user"] = User.query(User.email == self.request.get("email")).get() if self.tv["user"]: token = str(uuid.uuid4()) reset_token = PasswordResetToken(id = token) reset_token.email = self.tv["user"].email reset_token.token = token reset_token.expires = datetime.datetime.now() + datetime.timedelta(hours = 1) reset_token.put() send_reset_password_email(self.tv["user"], token) self.redirect('/forgot-pass?success='+ urllib.quote('Details about how to reset your password have been sent to you by email.')) else: self.redirect('/forgot-pass?error='+ urllib.quote('Invalid Email.')) return
def reset_password(): """ Ask for a reset password link by email. --- tags: - Accounts responses: 200: description: fixme. """ email = request.args.get("email", None) if not email: abort(400) user = User.query.filter(User.email == email).first() if not user: abort(404) # generate a reset link prt = PasswordResetToken() prt.token = generate_random_token() prt.expires_at = None prt.user_id = user.id db.session.add(prt) db.session.commit() add_user_log(user.id, user.id, "user", "info", "Password reset token generated") # Send email token_link = f"https://{current_app.config['AP_DOMAIN']}/password-reset/{prt.token}" msg = Message(subject="Password reset", recipients=[user.email], sender=current_app.config["MAIL_DEFAULT_SENDER"]) _config = Config.query.first() if not _config: print("ERROR: cannot get instance Config from database") instance = {"name": None, "url": None} if _config: instance["name"] = _config.app_name instance["url"] = current_app.config["REEL2BITS_URL"] msg.body = render_template("email/password_reset.txt", token_link=token_link, user=user, instance=instance) msg.html = render_template("email/password_reset.html", token_link=token_link, user=user, instance=instance) err = None mail = current_app.extensions.get("mail") if not mail: err = "mail extension is none" else: try: mail.send(msg) except ConnectionRefusedError as e: # TODO: do something about that maybe print(f"Error sending mail: {e}") err = e except smtplib.SMTPRecipientsRefused as e: print(f"Error sending mail: {e}") err = e except smtplib.SMTPException as e: print(f"Error sending mail: {e}") err = e if err: add_log( "global", "ERROR", f"Error sending email for password reset user {user.id}: {err}" ) add_user_log(user.id, user.id, "user", "error", "An error occured while sending email") return jsonify({"status": "ok"}), 204
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 })