diff --git a/app/routes.py b/app/routes.py index 0277876..d477e2d 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,6 +1,6 @@ from flask import Blueprint, render_template, request, flash, redirect, url_for, current_app from flask_mail import Message -from itsdangerous import URLSafeTimedSerializer, SignatureExpired, BadSignature +from itsdangerous import URLSafeTimedSerializer, SignatureExpired, BadSignature, exc from app.extensions import db, mail, limiter from app.models import Participant import traceback @@ -20,12 +20,14 @@ def add_user(name, email, looking_for_team, on_waitlist = False): def send_verification_email(user): s = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) - token = s.dumps(user.email, salt='email-confirm-h4tum-ctf-2025') - link = url_for('main.verify_email', token=token, _external=True) + register_token = s.dumps(user.email, salt='email-confirm-h4tum-ctf-2025') + remove_token = s.dumps(user.email, salt='email-remove-h4tum-ctf-2026') + register_link = url_for('main.verify_email', token=register_token, _external=True) + remove_link = url_for('main.remove_registration', token=remove_token, _external=True) logo = url_for('static', filename='h4tum_white.png', _external=True) msg = Message('Verify your CTF Registration', recipients=[user.email]) - msg.html = render_template("email.html", user_name=user.name, link=link, logo_url=logo) + msg.html = render_template("email.html", user_name=user.name, register_link=register_link, remove_link=remove_link, logo_url=logo) mail.send(msg) user.num_emails_sent += 1 db.session.commit() @@ -111,6 +113,31 @@ def verify_email(token): return render_template('success.html', message=msg, discord_link=discord_link, discord_token=discord_token) +@bp.route('/remove/') +@limiter.limit("5 per hour") +def remove_registration(token): + s = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) + try: + email = s.loads(token, salt='email-remove-h4tum-ctf-2026', max_age=1209600) + except SignatureExpired: + return render_template("error.html", error_message="Token expired. Please register again.") + except BadSignature: + return render_template("error.html", error_message="Invalid token.") + + user = Participant.query.filter_by(email=email).first_or_404() + + if not user: + return render_template("error.html", error_message="No user found.") + + try: + db.session.delete(user) + db.session.commit() + return render_template("deregistered.html", message="Sad to see you go. Comeback next time :D") + except: + db.session.rollback() + return render_template("error.html", error_message="Removing user did not work") + + @bp.route('/dsgvo') @limiter.exempt def dsgvo(): diff --git a/app/templates/deregistered.html b/app/templates/deregistered.html new file mode 100644 index 0000000..f378539 --- /dev/null +++ b/app/templates/deregistered.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% block content %} + + +
+

> Remove user

+

{{ message }}

+
+ +{% endblock %} diff --git a/app/templates/email.html b/app/templates/email.html index 546cdcd..7636e2d 100644 --- a/app/templates/email.html +++ b/app/templates/email.html @@ -30,7 +30,7 @@ @@ -39,6 +39,24 @@ + + + + + +
- + CONFIRM MY SPOT
+ If you can not attend please deregister using the following link +
+ + + + +
+ + DEREGISTER + +
+
If you didn't request this, you can safely ignore this email.