import argparse import base64 import binascii import json import secrets from urllib.parse import urlencode import yaml from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding, rsa from flask import Flask, redirect, render_template, request, session parser = argparse.ArgumentParser() parser.add_argument("config", help="Path to configuration file") args = parser.parse_args() with open(args.config) as f: config = yaml.safe_load(f) app = Flask(__name__) app.secret_key = config["secret_key"] private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) public_key = private_key.public_key().public_bytes( serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo, ).decode() @app.route("/") def index(): return render_template("index.html", application_name=config["application_name"]) @app.route("/authorize") def authorize(): nonce = secrets.token_hex(16) client_id = secrets.token_hex(48) session["nonce"] = nonce params = { "auth_redirect": config["redirect_url"], "application_name": config["application_name"], "scopes": "read", "client_id": client_id, "nonce": nonce, "public_key": public_key, } return redirect(f"{config['forum_url']}/user-api-key/new?{urlencode(params)}") @app.route("/callback") def callback(): payload_b64 = request.args.get("payload") if not payload_b64: return "Missing payload", 400 try: payload = base64.b64decode(payload_b64) except binascii.Error: return "Failed to decode payload", 400 try: data = private_key.decrypt(payload, padding.PKCS1v15()) response = json.loads(data) except (ValueError, json.JSONDecodeError): return "Failed to decrypt or decode payload", 400 if response.get("nonce") != session.get("nonce"): return "Invalid nonce", 400 key = response["key"] calendar_url = ( f"{config['forum_url']}/discourse-post-event/events.ics?" + urlencode({"order": "desc", "api_key": key}) ) return render_template( "result.html", application_name=config["application_name"], calendar_url=calendar_url, ) if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)