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 {}