#!/usr/bin/env python3 """ Database tools and functions. """ import string import urllib import functools import bleach from flask import session, request, abort, redirect, url_for, render_template from flask import Blueprint import database as db views = Blueprint("views", __name__) def login_required(url=None): """ A decorator function to protect certain endpoints by requiring the user to either pass a valid session cookie, or pass thier username and password along with the request to login. """ def actual_decorator(func): @functools.wraps(func) def _nop(*args, **kwargs): username = session.get("username") if db.verify_username(username): return func(*args, **kwargs) username = request.form.get("user") password = request.form.get("pass") if db.verify_password(username, password): return func(*args, **kwargs) if url: return redirect(url_for(url)) else: abort(401) return _nop return actual_decorator def sanitize_title(canon_title): """ Sanitizes the given canonical title for a quest and returns a url-friendly version. """ ident_title = canon_title.lower().replace(" ", "-") ident_title = urllib.parse.quote(ident_title) return ident_title @views.route("/quest/") def quest(quest_title): """ An arbituary quest page. """ quest_title, _, extra = quest_title.partition("/") res = db._DB.execute("SELECT * FROM `quests` WHERE `ident_title` = %s", (quest_title,)) data = res.fetchone() if not data: abort(404) quest_id, quest_title, _, owner_id, quest_data = data messages = db.get_chat_messages(quest_id) return render_template('quest.html', quest_title=quest_title, quest_body=quest_data, room_id=quest_id, messages=messages) @views.route("/create_quest", methods=["GET", "POST"]) @login_required("views.login") def create_quest(): """ Starts a new quest. """ if request.method == "GET": return render_template("create_quest.html") canon_title = request.form.get("quest_title") quest_body = request.form.get("quest_body") ident_title = sanitize_title(canon_title) quest_body = bleach.clean(quest_body.strip()) quest_body = quest_body.replace("\n", "
") db._DB.execute( "INSERT INTO `quests` (`canon_title`, `ident_title`, `quest_data`) " \ "VALUES (%s, %s, %s)", (canon_title, ident_title, quest_body)) return redirect(url_for('views.quest', quest_title=ident_title)) @views.route("/login", methods=["GET", "POST"]) def login(): """ Logs the user in. """ if request.method == "GET": return render_template("login.html") username = request.form.get("user") password = request.form.get("pass") if db.verify_password(username, password): session["username"] = username return redirect(url_for("views.index")) else: abort(401) @views.route("/signup", methods=["GET", "POST"]) def signup(): """ Create a new account. """ if request.method == "GET": return render_template("signup.html") username = request.form.get("user") password = request.form.get("pass") password_verify = request.form.get("pass_verify") if len(username) > 20: "username_too_long" elif len(username) < 3: "username_too_short" chrs = [c not in string.ascii_letters + string.digits for c in username] if any(chrs): "username_bad_chars" if db.verify_username(username): "username_taken" if len(password) > 1024: "password_too_long" elif len(password) < 8: "password_too_short" if password != password_verify: "passwords_dont_match" res = db.add_user(username, password) return redirect(url_for("views.index")) @views.route("/") def index(): """ The index page. """ return render_template("index.html")