from typing import Dict, Any from flask import ( Blueprint, request, redirect, Response, url_for, make_response, render_template, ) from bemani.common import CardCipher, CardCipherException, AESCipher, Time from bemani.frontend.app import ( loginrequired, loginprohibited, success, error, jsonify, valid_email, valid_username, valid_pin, render_react, ) from bemani.frontend.templates import templates_location from bemani.frontend.static import static_location from bemani.frontend.types import g account_pages = Blueprint( "account_pages", __name__, template_folder=templates_location, static_folder=static_location, ) @account_pages.route("/login", methods=["POST"]) @loginprohibited def login() -> Response: username = request.form["username"] password = request.form["password"] userid = g.data.local.user.from_username(username) if userid is None: error("Unrecognized username or password!") return Response( render_template( "account/login.html", **{"title": "Log In", "show_navigation": False, "username": username}, ) ) if g.data.local.user.validate_password(userid, password): aes = AESCipher(g.config.secret_key) sessionID = g.data.local.user.create_session(userid, expiration=90 * 86400) response = make_response(redirect(url_for("home_pages.viewhome"))) response.set_cookie( "SessionID", aes.encrypt(sessionID), expires=Time.now() + (90 * Time.SECONDS_IN_DAY), ) return response else: error("Unrecognized username or password!") return Response( render_template( "account/login.html", **{"title": "Log In", "show_navigation": False, "username": username}, ) ) @account_pages.route("/login") @loginprohibited def viewlogin() -> Response: return Response( render_template( "account/login.html", **{"title": "Log In", "show_navigation": False} ) ) def register_display(card_number: str, username: str, email: str) -> Response: return Response( render_template( "account/register.html", **{ "title": "Register New Account", "show_navigation": False, "card_number": card_number, "username": username, "email": email, }, ) ) @account_pages.route("/register", methods=["POST"]) @loginprohibited def register() -> Response: card_number = request.form["card_number"] pin = request.form["pin"] username = request.form["username"] email = request.form["email"] password1 = request.form["password1"] password2 = request.form["password2"] # First, try to convert the card to a valid E004 ID try: cardid = CardCipher.decode(card_number) except CardCipherException: error("Invalid card number!") return register_display(card_number, username, email) # Now, see if this card ID exists already userid = g.data.local.user.from_cardid(cardid) if userid is None: error("This card has not been used on the network yet!") return register_display(card_number, username, email) # Now, make sure this user doesn't already have an account user = g.data.local.user.get_user(userid) if user.username is not None or user.email is not None: error("This card is already in use!") return register_display(card_number, username, email) # Now, see if the pin is correct if not g.data.local.user.validate_pin(userid, pin): error("The entered PIN does not match the PIN on the card!") return register_display(card_number, username, email) # Now, see if the username is valid if not valid_username(username): error("Invalid username!") return register_display(card_number, username, email) # Now, check whether the username is already in use if g.data.local.user.from_username(username) is not None: error("The chosen username is already in use!") return register_display(card_number, username, email) # Now, see if the email address is valid if not valid_email(email): error("Invalid email address!") return register_display(card_number, username, email) # Now, make sure that the passwords match if password1 != password2: error("Passwords do not match each other!") return register_display(card_number, username, email) # Now, make sure passwords are long enough if len(password1) < 6: error("Password is not long enough!") return register_display(card_number, username, email) # Now, create the account. user.username = username user.email = email g.data.local.user.put_user(user) g.data.local.user.update_password(userid, password1) # Now, log them into that created account! aes = AESCipher(g.config.secret_key) sessionID = g.data.local.user.create_session(userid) success("Successfully registered account!") response = make_response(redirect(url_for("home_pages.viewhome"))) response.set_cookie("SessionID", aes.encrypt(sessionID)) return response @account_pages.route("/register") @loginprohibited def viewregister() -> Response: return Response( render_template( "account/register.html", **{"title": "Register New Account", "show_navigation": False}, ) ) @account_pages.route("/logout") @loginrequired def logout() -> Response: g.data.local.user.destroy_session(g.sessionID) response = make_response(redirect(url_for("account_pages.viewlogin"))) response.set_cookie("SessionID", "", expires=0) success("Successfully logged out!") return response @account_pages.route("/account") @loginrequired def viewaccount() -> Response: user = g.data.local.user.get_user(g.userID) return render_react( "Account Management", "account/account.react.js", { "email": user.email, "username": user.username, }, { "updateemail": url_for("account_pages.updateemail"), "updatepin": url_for("account_pages.updatepin"), "updatepassword": url_for("account_pages.updatepassword"), }, ) @account_pages.route("/account/cards") @loginrequired def viewcards() -> Response: cards = [CardCipher.encode(card) for card in g.data.local.user.get_cards(g.userID)] return render_react( "Card Management", "account/cards.react.js", { "cards": cards, }, { "addcard": url_for("account_pages.addcard"), "removecard": url_for("account_pages.removecard"), "listcards": url_for("account_pages.listcards"), }, ) @account_pages.route("/account/cards/list") @jsonify @loginrequired def listcards() -> Dict[str, Any]: # Return new card list cards = [CardCipher.encode(card) for card in g.data.local.user.get_cards(g.userID)] return { "cards": cards, } @account_pages.route("/account/cards/add", methods=["POST"]) @jsonify @loginrequired def addcard() -> Dict[str, Any]: # Grab card, convert it card = request.get_json()["card"] try: cardid = CardCipher.decode(card) except CardCipherException: raise Exception("Invalid card number!") # See if it is already claimed userid = g.data.local.user.from_cardid(cardid) if userid is not None: raise Exception("This card is already in use!") # Add it to this user's account g.data.local.user.add_card(g.userID, cardid) # Return new card list cards = [CardCipher.encode(card) for card in g.data.local.user.get_cards(g.userID)] return { "cards": cards, } @account_pages.route("/account/cards/remove", methods=["POST"]) @jsonify @loginrequired def removecard() -> Dict[str, Any]: # Grab card, convert it card = request.get_json()["card"] try: cardid = CardCipher.decode(card) except CardCipherException: raise Exception("Invalid card number!") # Make sure it is our card userid = g.data.local.user.from_cardid(cardid) if userid != g.userID: raise Exception("This card is not yours to delete!") # Remove it from this user's account g.data.local.user.destroy_card(g.userID, cardid) # Return new card list cards = [CardCipher.encode(card) for card in g.data.local.user.get_cards(g.userID)] return { "cards": cards, } @account_pages.route("/account/email/update", methods=["POST"]) @jsonify @loginrequired def updateemail() -> Dict[str, Any]: email = request.get_json()["email"] password = request.get_json()["password"] user = g.data.local.user.get_user(g.userID) if user is None: raise Exception("Unable to find user to update!") # Make sure current password matches if not g.data.local.user.validate_password(g.userID, password): raise Exception("Current password is not correct!") if not valid_email(email): raise Exception("Invalid email address!") # Update and save user.email = email g.data.local.user.put_user(user) # Return updated email return { "email": email, } @account_pages.route("/account/pin/update", methods=["POST"]) @jsonify @loginrequired def updatepin() -> Dict[str, Any]: pin = request.get_json()["pin"] user = g.data.local.user.get_user(g.userID) if user is None: raise Exception("Unable to find user to update!") if not valid_pin(pin, "card"): raise Exception("Invalid PIN, must be exactly 4 digits!") # Update and save g.data.local.user.update_pin(g.userID, pin) # Return nothing return {} @account_pages.route("/account/password/update", methods=["POST"]) @jsonify @loginrequired def updatepassword() -> Dict[str, Any]: old = request.get_json()["old"] new1 = request.get_json()["new1"] new2 = request.get_json()["new2"] user = g.data.local.user.get_user(g.userID) if user is None: raise Exception("Unable to find user to update!") # Make sure current password matches if not g.data.local.user.validate_password(g.userID, old): raise Exception("Current password is not correct!") # Now, make sure that the passwords match if new1 != new2: raise Exception("Passwords do not match each other!") # Now, make sure passwords are long enough if len(new1) < 6: raise Exception("Password is not long enough!") # Update and save g.data.local.user.update_password(g.userID, new1) # Return nothing return {}