327 lines
10 KiB
Python
327 lines
10 KiB
Python
import datetime
|
|
from typing import Dict, Any
|
|
from flask import Blueprint, request, redirect, Response, url_for, make_response, render_template, g # type: ignore
|
|
|
|
from bemani.common import CardCipher, CardCipherException, AESCipher
|
|
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
|
|
|
|
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=datetime.datetime.now() + datetime.timedelta(days=90)
|
|
)
|
|
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 {}
|