|
|
|
from datetime import datetime
|
|
|
|
from flask import render_template, flash, redirect, request, url_for, abort, Markup, escape
|
|
|
|
from flask_login import login_user, logout_user, current_user, login_required
|
|
|
|
|
|
|
|
from app import db
|
|
|
|
from app.auth import bp
|
|
|
|
from app.utils import generate_qr_code, serve_pil_image
|
|
|
|
from app.models import User
|
|
|
|
from app.auth.forms import LoginForm, RegistrationForm, ChangePasswordForm, SetPasswordForm
|
|
|
|
|
|
|
|
@bp.route('/login', methods=['GET', 'POST'])
|
|
|
|
def login():
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
return redirect(url_for('main.index'))
|
|
|
|
form = LoginForm()
|
|
|
|
if form.validate_on_submit():
|
|
|
|
user = User.query.filter_by(name=form.username.data).first()
|
|
|
|
if user is None or not user.check_password(form.password.data):
|
|
|
|
flash('Invalid username or password')
|
|
|
|
return redirect(url_for('auth.login'))
|
|
|
|
return user_login(user, form.remember_me.data)
|
|
|
|
return render_template('auth/login.html', title='Sign In', form=form)
|
|
|
|
|
|
|
|
def user_login(user, remember):
|
|
|
|
login_user(user, remember)
|
|
|
|
user.last_login = datetime.utcnow()
|
|
|
|
return redirect(url_for('main.index'))
|
|
|
|
|
|
|
|
@bp.route('/logout')
|
|
|
|
@login_required
|
|
|
|
def logout():
|
|
|
|
logout_user()
|
|
|
|
return redirect(url_for('main.index'))
|
|
|
|
|
|
|
|
@bp.route('/register', methods=['GET', 'POST'])
|
|
|
|
def register():
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
return redirect(url_for('main.index'))
|
|
|
|
form = RegistrationForm()
|
|
|
|
if form.validate_on_submit():
|
|
|
|
user = User(name=form.username.data)
|
|
|
|
user.set_password(form.password.data)
|
|
|
|
user.set_auth_hash()
|
|
|
|
user.last_login = datetime.utcnow()
|
|
|
|
db.session.add(user)
|
|
|
|
db.session.commit()
|
|
|
|
flash('Congratulations, you are now a registered user!')
|
|
|
|
return redirect(url_for('auth.login'))
|
|
|
|
return render_template('auth/register.html', title='Register', form=form)
|
|
|
|
|
|
|
|
@bp.route('/generate_auth_hash/<username>')
|
|
|
|
def generate_auth_hash(username):
|
|
|
|
user = User.query.filter(User.name == username).first_or_404()
|
|
|
|
if not current_user.owns_game_played_by(user):
|
|
|
|
abort(403)
|
|
|
|
if user.auth_hash:
|
|
|
|
flash('Auth hash is already generated')
|
|
|
|
abort(403)
|
|
|
|
if user.password_hash:
|
|
|
|
flash('After a player has created a password, it is no longer possible to generate a QR code.')
|
|
|
|
abort(403)
|
|
|
|
user.set_auth_hash()
|
|
|
|
db.session.commit()
|
|
|
|
return 'nothing'
|
|
|
|
|
|
|
|
@bp.route('/user/<auth_hash>')
|
|
|
|
def user_hash_login(auth_hash):
|
|
|
|
user = User.query.filter_by(auth_hash=auth_hash).first_or_404()
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
if current_user.owns_game_played_by(user):
|
|
|
|
safe_username = escape(user.name)
|
|
|
|
flash(Markup(f"The <a href='{request.url}'>login link</a> for {safe_username} works! "
|
|
|
|
"However, logged in users like you can't access it"))
|
|
|
|
return redirect(url_for('main.game_player', game_name=current_user.owned_game_played_by(user).name, username=user.name))
|
|
|
|
flash(f'You are aleady logged in as {current_user.name}!')
|
|
|
|
return redirect(url_for('main.index'))
|
|
|
|
if user.password_hash:
|
|
|
|
flash('Please login with your username and password!')
|
|
|
|
abort(404)
|
|
|
|
if user.last_login:
|
|
|
|
return user_login(user, True)
|
|
|
|
if request.args.get('login', default='false', type=str).lower() == 'true':
|
|
|
|
return user_login(user, True)
|
|
|
|
return render_template('auth/user_hash_login.html', title=f'User: {user.name}', user=user)
|
|
|
|
|
|
|
|
@bp.route('/user/<auth_hash>/qrcode.png')
|
|
|
|
@login_required
|
|
|
|
def user_qrcode(auth_hash):
|
|
|
|
user = User.query.filter_by(auth_hash=auth_hash).first_or_404()
|
|
|
|
if not current_user.owns_game_played_by(user):
|
|
|
|
abort(403)
|
|
|
|
if user.password_hash:
|
|
|
|
flash('After a player has created a password, it is no longer possible to request their QR code.')
|
|
|
|
abort(403)
|
|
|
|
img = generate_qr_code(url_for('auth.user_hash_login', auth_hash=auth_hash, _external=True))
|
|
|
|
return serve_pil_image(img)
|
|
|
|
|
|
|
|
@bp.route('/set_password', methods=['GET', 'POST'])
|
|
|
|
@bp.route('/change_password', methods=['GET', 'POST'])
|
|
|
|
def change_password():
|
|
|
|
auth_hash = request.args.get('auth_hash', default=None, type=str)
|
|
|
|
if auth_hash:
|
|
|
|
user = User.query.filter_by(auth_hash=auth_hash).first_or_404()
|
|
|
|
login_user(user, True)
|
|
|
|
user.last_login = datetime.utcnow()
|
|
|
|
else:
|
|
|
|
if not current_user.is_authenticated:
|
|
|
|
abort(403)
|
|
|
|
user = User.query.filter_by(name=current_user.name).first_or_404()
|
|
|
|
no_old_password = not user.password_hash
|
|
|
|
if no_old_password:
|
|
|
|
form = SetPasswordForm()
|
|
|
|
else:
|
|
|
|
form = ChangePasswordForm()
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
if not no_old_password:
|
|
|
|
if not user.check_password(form.old_password.data):
|
|
|
|
flash('Invalid password')
|
|
|
|
return redirect(url_for('auth.change_password'))
|
|
|
|
user.set_password(form.new_password.data)
|
|
|
|
db.session.commit()
|
|
|
|
if no_old_password:
|
|
|
|
flash('Your password was set')
|
|
|
|
else:
|
|
|
|
flash('Your password was changed!')
|
|
|
|
return redirect(url_for('main.index'))
|
|
|
|
return render_template('auth/change_password.html', form=form)
|