# -*- coding: utf-8 -*- import sys import traceback from flask import Blueprint from flask import redirect, render_template, request, flash, url_for from flask_login import login_required, login_user, current_user from werkzeug.exceptions import Forbidden from werkzeug import Response from accounts.forms import ( RegisterForm, RegisterCompleteForm, LostPasswordForm, SettingsForm, ) from accounts.utils import templated from accounts.utils.confirmation import Confirmation from accounts.utils.login import logout_required from accounts.models import Account from accounts.app import accounts_app from typing import Union bp = Blueprint("default", __name__) @bp.route("/register", methods=["GET", "POST"]) @templated("register.html") @logout_required def register() -> Union[dict, Response]: form = RegisterForm() if form.validate_on_submit(): accounts_app.mail_backend.send( form.mail.data, "mail/register.txt", username=form.username.data ) flash( "Es wurde eine E-Mail an die angegebene Adresse geschickt, " "um diese zu überprüfen. Bitte folge den Anweisungen in der " "E-Mail.", "success", ) return redirect(url_for(".index")) return {"form": form} @bp.route("/register/", methods=["GET", "POST"]) @templated("register_complete.html") @logout_required def register_complete(token: str): # TODO: check for double uids and mail username, mail = Confirmation("register").loads_http( token, max_age=3 * 24 * 60 * 60 ) try: accounts_app.user_backend.get_by_uid(username) accounts_app.user_backend.get_by_mail(mail) except accounts_app.user_backend.NoSuchUserError: pass else: flash( "Du hast den Benutzer bereits angelegt! Du kannst dich jetzt einfach einloggen:" ) return redirect(url_for(".index")) form = RegisterCompleteForm() if form.validate_on_submit(): user = Account(username, mail, password=form.password.data) accounts_app.user_backend.register(user) login_user(user) accounts_app.mail_backend.send( accounts_app.config["MAIL_REGISTER_NOTIFY"], "mail/register_notify.txt", username=username, mail=mail, ) flash("Benutzer erfolgreich angelegt.", "success") return redirect(url_for(".index")) return { "form": form, "token": token, "username": username, "mail": mail, } @bp.route("/lost_password", methods=["GET", "POST"]) @templated("lost_password.html") @logout_required def lost_password(): form = LostPasswordForm() if form.validate_on_submit() and form.user: # TODO: make the link only usable once (e.g include a hash of the old pw) # atm the only thing we do is make the link valid for only little time accounts_app.mail_backend.send( form.user.mail, "mail/lost_password.txt", username=form.user.uid ) flash( "Wir haben dir eine E-Mail mit einem Link zum Passwort ändern " "geschickt. Bitte folge den Anweisungen in der E-Mail.", "success", ) return redirect(url_for(".index")) return {"form": form} @bp.route("/lost_password/", methods=["GET", "POST"]) @templated("lost_password_complete.html") @logout_required def lost_password_complete(token: str): (username,) = Confirmation("lost_password").loads_http( token, max_age=4 * 60 * 60 ) form = RegisterCompleteForm() if form.validate_on_submit(): user = accounts_app.user_backend.get_by_uid(username) user.change_password(form.password.data) accounts_app.user_backend.update(user, as_admin=True) login_user(user) flash("Passwort geändert.", "success") return redirect(url_for(".index")) return { "form": form, "token": token, "username": username, } @bp.route("/", methods=["GET", "POST"]) @templated("index.html") @login_required def index() -> Union[Response, dict]: form = SettingsForm(mail=current_user.mail) if form.validate_on_submit(): changed = False if request.form.get("submit_main"): if form.mail.data and form.mail.data != current_user.mail: accounts_app.mail_backend.send( form.mail.data, "mail/change_mail.txt", username=current_user.uid, ) flash( "Es wurde eine E-Mail an die angegebene Adresse geschickt, " "um diese zu überprüfen. Bitte folge den Anweisungen in der " "E-Mail.", "success", ) changed = True if form.password.data: current_user.change_password( form.password.data, form.old_password.data ) flash("Passwort geändert", "success") changed = True if changed: accounts_app.user_backend.update(current_user) login_user(current_user) return redirect(url_for(".index")) else: flash("Nichts geändert.") return { "form": form, } @bp.route("/change_mail/") @login_required def change_mail(token: str): username, mail = Confirmation("change_mail").loads_http( token, max_age=3 * 24 * 60 * 60 ) if current_user.uid != username: raise Forbidden( "Bitte logge dich als der Benutzer ein, dessen E-Mail-Adresse du ändern willst." ) results = accounts_app.user_backend.find_by_mail(mail) for user in results: if user.uid != current_user.uid: raise Forbidden( "Diese E-Mail-Adresse wird schon von einem anderen account benutzt!" ) current_user.change_email(mail) accounts_app.user_backend.update(current_user) flash("E-Mail-Adresse geändert.", "success") return redirect(url_for(".index")) @bp.route("/about") @templated("about.html") def about(): return { "app": accounts_app, } @bp.app_errorhandler(Exception) def exception_handler(e): traceback.print_exception(e, file=sys.stderr) return ( render_template( "error.html", error={ "message": "Interner Fehler", "description": "Bitte melde uns den Fehler an " + """spline@spline.de.""", }, ), 500, ) @bp.app_errorhandler(403) @bp.app_errorhandler(404) def errorhandler(e): return render_template("error.html", error=e), e.code