# ContentDB
# Copyright (C) 2020 rubenwardy
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see
We were unable to perform the password reset as we could not find an account associated with this email.
This may be because you used another email with your account, or because you never confirmed your email.
You can use GitHub to log in if it is associated with your account. Otherwise, you may need to contact rubenwardy for help.
If you weren't expecting to receive this email, then you can safely ignore it.
""") return redirect(url_for("flatpage", path="email_sent")) return render_template("users/forgot_password.html", form=form) class SetPasswordForm(FlaskForm): email = StringField(lazy_gettext("Email"), [Optional(), Email()]) password = PasswordField(lazy_gettext("New password"), [InputRequired(), Length(8, 100)]) password2 = PasswordField(lazy_gettext("Verify password"), [InputRequired(), Length(8, 100), validators.EqualTo('password', message=lazy_gettext('Passwords must match'))]) submit = SubmitField(lazy_gettext("Save")) class ChangePasswordForm(FlaskForm): old_password = PasswordField(lazy_gettext("Old password"), [InputRequired(), Length(8, 100)]) password = PasswordField(lazy_gettext("New password"), [InputRequired(), Length(8, 100)]) password2 = PasswordField(lazy_gettext("Verify password"), [InputRequired(), Length(8, 100), validators.EqualTo('password', message=lazy_gettext('Passwords must match'))]) submit = SubmitField(lazy_gettext("Save")) def handle_set_password(form): one = form.password.data two = form.password2.data if one != two: flash(gettext("Passwords do not match"), "danger") return addAuditLog(AuditSeverity.USER, current_user, "Changed their password", url_for("users.profile", username=current_user.username)) current_user.password = make_flask_login_password(form.password.data) if hasattr(form, "email"): newEmail = nonEmptyOrNone(form.email.data) if newEmail and newEmail != current_user.email: if EmailSubscription.query.filter_by(email=form.email.data, blacklisted=True).count() > 0: flash(gettext(u"That email address has been unsubscribed/blacklisted, and cannot be used"), "danger") return user_by_email = User.query.filter_by(email=form.email.data).first() if user_by_email: send_anon_email.delay(form.email.data, gettext("Email already in use"), gettext(u"We were unable to create the account as the email is already in use by %(display_name)s. Try a different email address.", display_name=user_by_email.display_name)) else: token = randomString(32) ver = UserEmailVerification() ver.user = current_user ver.token = token ver.email = newEmail db.session.add(ver) db.session.commit() send_verify_email.delay(form.email.data, token) flash(gettext("Your password has been changed successfully."), "success") return redirect(url_for("flatpage", path="email_sent")) db.session.commit() flash(gettext("Your password has been changed successfully."), "success") return redirect(url_for("homepage.home")) @bp.route("/user/change-password/", methods=["GET", "POST"]) @login_required def change_password(): form = ChangePasswordForm(request.form) if form.validate_on_submit(): if check_password_hash(current_user.password, form.old_password.data): ret = handle_set_password(form) if ret: return ret else: flash(gettext("Old password is incorrect"), "danger") return render_template("users/change_set_password.html", form=form, suggested_password=genphrase(entropy=52, wordset="bip39")) @bp.route("/user/set-password/", methods=["GET", "POST"]) @login_required def set_password(): if current_user.password: return redirect(url_for("users.change_password")) form = SetPasswordForm(request.form) if current_user.email is None: form.email.validators = [InputRequired(), Email()] if form.validate_on_submit(): ret = handle_set_password(form) if ret: return ret return render_template("users/change_set_password.html", form=form, optional=request.args.get("optional"), suggested_password=genphrase(entropy=52, wordset="bip39")) @bp.route("/user/verify/") def verify_email(): token = request.args.get("token") ver: UserEmailVerification = UserEmailVerification.query.filter_by(token=token).first() if ver is None: flash(gettext("Unknown verification token!"), "danger") return redirect(url_for("homepage.home")) delta = (datetime.datetime.now() - ver.created_at) delta: datetime.timedelta if delta.total_seconds() > 12*60*60: flash(gettext("Token has expired"), "danger") db.session.delete(ver) db.session.commit() return redirect(url_for("homepage.home")) user = ver.user addAuditLog(AuditSeverity.USER, user, "Confirmed their email", url_for("users.profile", username=user.username)) was_activating = not user.is_active if ver.email and user.email != ver.email: if User.query.filter_by(email=ver.email).count() > 0: flash(gettext("Another user is already using that email"), "danger") return redirect(url_for("homepage.home")) flash(gettext("Confirmed email change"), "success") if user.email: send_user_email.delay(user.email, gettext("Email address changed"), gettext("Your email address has changed. If you didn't request this, please contact an administrator.")) user.is_active = True user.email = ver.email db.session.delete(ver) db.session.commit() if ver.is_password_reset: login_user(user) user.password = None db.session.commit() return redirect(url_for("users.set_password")) if current_user.is_authenticated: return redirect(url_for("users.profile", username=current_user.username)) elif was_activating: flash(gettext("You may now log in"), "success") return redirect(url_for("users.login")) else: return redirect(url_for("homepage.home")) class UnsubscribeForm(FlaskForm): email = StringField(lazy_gettext("Email"), [InputRequired(), Email()]) submit = SubmitField(lazy_gettext("Send")) def unsubscribe_verify(): form = UnsubscribeForm(request.form) if form.validate_on_submit(): email = form.email.data sub = EmailSubscription.query.filter_by(email=email).first() if not sub: sub = EmailSubscription(email) db.session.add(sub) sub.token = randomString(32) db.session.commit() send_unsubscribe_verify.delay(form.email.data) return redirect(url_for("flatpage", path="email_sent")) return render_template("users/unsubscribe.html", form=form) def unsubscribe_manage(sub: EmailSubscription): user = User.query.filter_by(email=sub.email).first() if request.method == "POST": if user: user.email = None sub.blacklisted = True db.session.commit() flash(gettext("That email is now blacklisted. Please contact an admin if you wish to undo this."), "success") return redirect(url_for("homepage.home")) return render_template("users/unsubscribe.html", user=user) @bp.route("/unsubscribe/", methods=["GET", "POST"]) def unsubscribe(): token = request.args.get("token") if token: sub = EmailSubscription.query.filter_by(token=token).first() if sub: return unsubscribe_manage(sub) return unsubscribe_verify()