266 lines
11 KiB
Python
266 lines
11 KiB
Python
|
from flask import Blueprint, render_template, flash, redirect, request, jsonify, abort
|
||
|
from flask.helpers import url_for
|
||
|
from functools import wraps
|
||
|
|
||
|
from werkzeug.security import check_password_hash
|
||
|
from security.database import decrypt_find, decrypt_find_one
|
||
|
from .user.models import User
|
||
|
from flask_mail import Message
|
||
|
from main import db
|
||
|
from uuid import uuid4
|
||
|
import secrets
|
||
|
from main import mail
|
||
|
from datetime import datetime, timedelta
|
||
|
|
||
|
from .forms import CreateUserForm
|
||
|
|
||
|
cookie_consent = Blueprint(
|
||
|
'cookie_consent',
|
||
|
__name__
|
||
|
)
|
||
|
@cookie_consent.route('/')
|
||
|
def _cookies():
|
||
|
resp = redirect('/')
|
||
|
resp.set_cookie(
|
||
|
key = 'cookie_consent',
|
||
|
value = 'True',
|
||
|
max_age = timedelta(days=14) if request.cookies.get('remember') == 'True' else 'Session',
|
||
|
path = '/',
|
||
|
expires = datetime.utcnow() + timedelta(days=14) if request.cookies.get('remember') else 'Session'
|
||
|
)
|
||
|
return resp
|
||
|
|
||
|
views = Blueprint(
|
||
|
'admin_views',
|
||
|
__name__,
|
||
|
template_folder='templates',
|
||
|
static_folder='static'
|
||
|
)
|
||
|
|
||
|
def admin_account_required(function):
|
||
|
@wraps(function)
|
||
|
def decorated_function(*args, **kwargs):
|
||
|
if not db.users.find_one({}):
|
||
|
flash('No administrator accounts have been registered. Please register an administrator account.', 'alert')
|
||
|
return redirect(url_for('admin_auth.register'))
|
||
|
return function(*args, **kwargs)
|
||
|
return decorated_function
|
||
|
|
||
|
def disable_on_registration(function):
|
||
|
@wraps(function)
|
||
|
def decorated_function(*args, **kwargs):
|
||
|
if db.users.find_one({}):
|
||
|
return abort(404)
|
||
|
return function(*args, **kwargs)
|
||
|
return decorated_function
|
||
|
|
||
|
def get_id_from_cookie():
|
||
|
return request.cookies.get('_id')
|
||
|
|
||
|
def get_user_from_db(_id):
|
||
|
return db.users.find_one({'_id': _id})
|
||
|
|
||
|
def check_login():
|
||
|
_id = get_id_from_cookie()
|
||
|
return True if get_user_from_db(_id) else False
|
||
|
|
||
|
def login_required(function):
|
||
|
@wraps(function)
|
||
|
def decorated_function(*args, **kwargs):
|
||
|
if not check_login():
|
||
|
flash('Please log in to view this page.', 'alert')
|
||
|
return redirect(url_for('admin_auth.login'))
|
||
|
return function(*args, **kwargs)
|
||
|
return decorated_function
|
||
|
|
||
|
def disable_if_logged_in(function):
|
||
|
@wraps(function)
|
||
|
def decorated_function(*args, **kwargs):
|
||
|
if check_login():
|
||
|
return abort(404)
|
||
|
return function(*args, **kwargs)
|
||
|
return decorated_function
|
||
|
|
||
|
@views.route('/')
|
||
|
@views.route('/home/')
|
||
|
@views.route('/dashboard/')
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def home():
|
||
|
return render_template('/admin/index.html')
|
||
|
|
||
|
@views.route('/settings/')
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def settings():
|
||
|
return render_template('/admin/settings/index.html')
|
||
|
|
||
|
@views.route('/settings/users/', methods=['GET','POST'])
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def users():
|
||
|
form = CreateUserForm()
|
||
|
if request.method == 'GET':
|
||
|
users_list = decrypt_find(db.users, {})
|
||
|
return render_template('/admin/settings/users.html', users = users_list, form = form)
|
||
|
if request.method == 'POST':
|
||
|
if form.validate_on_submit():
|
||
|
entry = User(
|
||
|
_id = uuid4().hex,
|
||
|
username = request.form.get('username').lower(),
|
||
|
email = request.form.get('email'),
|
||
|
password = request.form.get('password') if not request.form.get('password') == '' else secrets.token_hex(12),
|
||
|
)
|
||
|
email = Message(
|
||
|
subject = 'RefTest | Registration Confirmation',
|
||
|
recipients = [entry.email],
|
||
|
body = f"""
|
||
|
Hello {entry.username}, \n\n
|
||
|
You have been registered as an administrator for the SKA RefTest App!\n\n
|
||
|
You can access your account using the username '{entry.username}'.\n\n
|
||
|
Your password is as follows:\n\n
|
||
|
{entry.password}\n\n
|
||
|
You can change your password by logging in to the admin console by copying the following URL into a web browser:\n\n
|
||
|
{url_for('admin_views.home', _external = True)}\n\n
|
||
|
Have a nice day.
|
||
|
""",
|
||
|
html = f"""
|
||
|
<p>Hello {entry.username},</p>
|
||
|
<p>You have been registered as an administrator for the SKA RefTest App!</p>
|
||
|
<p>You can access your account using the username '{entry.username}'.</p>
|
||
|
<p>Your password is as follows:</p>
|
||
|
<strong>{entry.password}</strong>
|
||
|
<p>You can change your password by logging in to the admin console at the link below:</p>
|
||
|
<p><a href='{url_for('admin_views.home', _external = True)}'>{url_for('admin_views.home', _external = True)}</a></p>
|
||
|
<p>Have a nice day.</p>
|
||
|
"""
|
||
|
)
|
||
|
mail.send(email)
|
||
|
return entry.register()
|
||
|
else:
|
||
|
errors = [*form.username.errors, *form.email.errors, *form.password.errors]
|
||
|
return jsonify({ 'error': errors}), 400
|
||
|
|
||
|
@views.route('/settings/users/delete/<string:_id>', methods = ['GET', 'POST'])
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def delete_user(_id:str):
|
||
|
if _id == get_id_from_cookie():
|
||
|
flash('Cannot delete your own user account.', 'error')
|
||
|
return redirect(url_for('admin_views.users'))
|
||
|
from .forms import DeleteUserForm
|
||
|
form = DeleteUserForm()
|
||
|
user = decrypt_find_one(db.users, {'_id': _id})
|
||
|
if request.method == 'GET':
|
||
|
if not user:
|
||
|
return abort(404)
|
||
|
return render_template('/admin/settings/delete-user.html', form = form, _id = _id, user = user)
|
||
|
if request.method == 'POST':
|
||
|
if not user:
|
||
|
return jsonify({ 'error': 'User does not exist.' }), 404
|
||
|
if form.validate_on_submit():
|
||
|
_user = decrypt_find_one(db.users, {'_id': get_id_from_cookie()})
|
||
|
password = request.form.get('password')
|
||
|
if not check_password_hash(_user['password'], password):
|
||
|
return jsonify({ 'error': 'The password you entered is incorrect.' }), 401
|
||
|
if request.form.get('notify'):
|
||
|
email = Message(
|
||
|
subject = 'RefTest | Account Deletion',
|
||
|
recipients = [user['email']],
|
||
|
bcc = [_user['email']],
|
||
|
body = f"""
|
||
|
Hello {user['username']}, \n\n
|
||
|
Your administrator account for the SKA RefTest App has been deleted by {_user['username']}. All data about your account has been deleted.\n\n
|
||
|
If you believe this was done in error, please contact them immediately.\n\n
|
||
|
If you would like to register to administer the app, please ask an existing administrator to create a new account.\n\n
|
||
|
Have a nice day.
|
||
|
""",
|
||
|
html = f"""
|
||
|
<p>Hello {user['username']},</p>
|
||
|
<p>Your administrator account for the SKA RefTest App has been deleted by {_user['username']}. All data about your account has been deleted.</p>
|
||
|
<p>If you believe this was done in error, please contact them immediately.</p>
|
||
|
<p>If you would like to register to administer the app, please ask an existing administrator to create a new account.</p>
|
||
|
<p>Have a nice day.</p>
|
||
|
"""
|
||
|
)
|
||
|
mail.send(email)
|
||
|
user = User(
|
||
|
_id = user['_id']
|
||
|
)
|
||
|
return user.delete()
|
||
|
else: return abort(400)
|
||
|
|
||
|
@views.route('/settings/users/update/<string:_id>', methods = ['GET', 'POST'])
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def update_user(_id:str):
|
||
|
if _id == get_id_from_cookie():
|
||
|
flash('Cannot delete your own user account.', 'error')
|
||
|
return redirect(url_for('admin_views.users'))
|
||
|
from .forms import UpdateUserForm
|
||
|
form = UpdateUserForm()
|
||
|
user = decrypt_find_one( db.users, {'_id': _id})
|
||
|
if request.method == 'GET':
|
||
|
if not user:
|
||
|
return abort(404)
|
||
|
return render_template('/admin/settings/update-user.html', form = form, _id = _id, user = user)
|
||
|
if request.method == 'POST':
|
||
|
if not user:
|
||
|
return jsonify({ 'error': 'User does not exist.' }), 404
|
||
|
if form.validate_on_submit():
|
||
|
_user = decrypt_find_one(db.users, {'_id': get_id_from_cookie()})
|
||
|
password = request.form.get('password')
|
||
|
if not check_password_hash(_user['password'], password):
|
||
|
return jsonify({ 'error': 'The password you entered is incorrect.' }), 401
|
||
|
if request.form.get('notify'):
|
||
|
recipient = request.form.get('email') if not request.form.get('email') == '' else user['email']
|
||
|
email = Message(
|
||
|
subject = 'RefTest | Account Update',
|
||
|
recipients = [recipient],
|
||
|
bcc = [_user['email']],
|
||
|
body = f"""
|
||
|
Hello {user['username']}, \n\n
|
||
|
Your administrator account for the SKA RefTest App has been updated by {_user['username']}.\n\n
|
||
|
Your new account details are as follows:\n\n
|
||
|
Email: {recipient}\n
|
||
|
Password: {request.form.get('password')}\n\n
|
||
|
You can update your email and password by logging in to the app.\n\n
|
||
|
Have a nice day.
|
||
|
""",
|
||
|
html = f"""
|
||
|
<p>Hello {user['username']},</p>
|
||
|
<p>Your administrator account for the SKA RefTest App has been updated by {_user['username']}.</p>
|
||
|
<p>Your new account details are as follows:</p>
|
||
|
<p>Email: {recipient} <br/>Password: {request.form.get('password')}</p>
|
||
|
<p>You can update your email and password by logging in to the app.</p>
|
||
|
<p>Have a nice day.</p>
|
||
|
"""
|
||
|
)
|
||
|
mail.send(email)
|
||
|
entry = User(
|
||
|
_id = _id,
|
||
|
email = request.form.get('email'),
|
||
|
password = request.form.get('password')
|
||
|
)
|
||
|
return entry.update()
|
||
|
else:
|
||
|
errors = [*form.user_password.errors, *form.email.errors, *form.password.errors, *form.password_reenter.errors]
|
||
|
return jsonify({ 'error': errors}), 400
|
||
|
|
||
|
@views.route('/settings/questions/')
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def questions():
|
||
|
return render_template('/admin/settings/questions.html')
|
||
|
|
||
|
@views.route('/settings/questions/upload/')
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def upload_questions():
|
||
|
return render_template('/admin/settings/upload-questions.html')
|
||
|
|
||
|
@views.route('/tests/')
|
||
|
@admin_account_required
|
||
|
@login_required
|
||
|
def tests():
|
||
|
return render_template('/admin/tests.html')
|