from flask import Flask, render_template, request, g, jsonify import sqlite3 import hashcash import freenode hash_difficulty = 4 hash_expiry = 7 # Days def get_db(): db = getattr(g, "database", None) if db is None: db = g.database = sqlite3.connect("database.db", detect_types=sqlite3.PARSE_DECLTYPES) db.row_factory = sqlite3.Row db.cursor().execute("PRAGMA foreign_keys = ON;") db.commit() return db app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/register", methods=["POST"]) def register(): try: token = request.form["token"] username = request.form["username"] password = request.form["password"] fingerprint = request.form["fingerprint"] except KeyError as e: return jsonify({"status": "failure", "reason": "incomplete registration details", "token_reusable": True}) database = get_db() # Verify Token # Failure? # Return failed page. # Insert Token # Failure? # Return failed page. # Register user # Success? # Commit token # Return success page # Failure? # Rollback Token # Return failure page (with token for re-use) try: token_valid = hashcash.hashcash_check(token, "freenode.net", hash_difficulty, hash_expiry * 24 * 60 * 60) except AssertionError as e: return jsonify({"status": "failure", "reason": str(e), "token_reusable": False}) if not token_valid: return jsonify({"status": "failure", "reason": "invalid token", "token_reusable": False}) cursor = database.cursor() try: cursor.execute("insert into tokens (token, entry_time) values (?, datetime('now'));", (token,)) except sqlite3.IntegrityError: database.rollback() return jsonify({"status": "failure", "reason": "token has already been used", "token_reusable": False}) except Exception as e: database.rollback() return jsonify({"status": "failure", "reason": str(e)}) try: freenode.register_user(username, password, certfp=fingerprint) database.commit() except NotImplementedError as e: # registration_error database.rollback() # Raise error page with appropriate info (username in use, invalid chars, etc) return jsonify({"status": "failure", "reason": str(e), "token_reusable": True}) except Exception as e: database.rollback() # Raise error page with generic info (also token still valid) return jsonify({"status": "failure", "reason": str(e), "token_reusable": True}) return jsonify({"status": "success", "username": username, "token_reusable": False}) def cleanup(): # Should run every day database = get_db() cursor = database.cursor() cursor.execute("delete from tokens where entry_time < datetime('now', ?);", ("-"+hash_expiry+" days",)) database.commit()