email confirmations

master
Fox Wilson 2015-11-08 21:44:04 -05:00
parent 31b82cdd30
commit 3ffbbce551
6 changed files with 88 additions and 5 deletions

35
app.py
View File

@ -84,8 +84,13 @@ def register():
team_elig = "team_eligibility" in request.form
affiliation = request.form["affiliation"]
team_key = utils.generate_team_key()
team = Team.create(name=team_name, email=team_email, eligible=team_elig, affiliation=affiliation, key=team_key)
confirmation_key = utils.generate_confirmation_key()
team = Team.create(name=team_name, email=team_email, eligible=team_elig, affiliation=affiliation, key=team_key,
email_confirmation_key=confirmation_key)
TeamAccess.create(team=team, ip=utils.get_ip(), time=datetime.now())
utils.send_confirmation_email(team_email, confirmation_key, team_key)
session["team_id"] = team.id
flash("Team created.")
return redirect(url_for('dashboard'))
@ -107,6 +112,17 @@ def assign_random():
# Things that require a team
@app.route('/confirm_email/', methods=["POST"])
@utils.login_required
def confirm_email():
if request.form["confirmation_key"] == g.team.email_confirmation_key:
flash("Email confirmed!")
g.team.email_confirmed = True
g.team.save()
else:
flash("Incorrect confirmation key.")
return redirect(url_for('dashboard'))
@app.route('/team/', methods=["GET", "POST"])
@utils.login_required
def dashboard():
@ -125,17 +141,28 @@ def dashboard():
team_email = request.form["team_email"]
affiliation = request.form["affiliation"]
team_elig = "team_eligibility" in request.form
email_changed = (team_email != g.team.email)
g.team.name = team_name
g.team.email = team_email
g.team.affiliation = affiliation
g.team.eligible = team_elig
if email_changed:
g.team.email_confirmation_key = utils.generate_confirmation_key()
g.team.email_confirmed = False
utils.send_confirmation_email(team_email, g.team.email_confirmation_key, g.team.key)
flash("Changes saved. Please check your email for a new confirmation key.")
else:
flash("Changes saved.")
g.team.save()
flash("Changes saved.")
return redirect(url_for('dashboard'))
@app.route('/challenges/')
@utils.competition_running_required
@utils.login_required
@utils.confirmed_email_required
def challenges():
chals = Challenge.select().order_by(Challenge.points)
solved = Challenge.select().join(ChallengeSolve).where(ChallengeSolve.team == g.team)
@ -143,7 +170,7 @@ def challenges():
@app.route('/submit/<int:challenge>/', methods=["POST"])
@utils.competition_running_required
@utils.login_required
@utils.confirmed_email_required
def submit(challenge):
chal = Challenge.get(Challenge.id == challenge)
flag = request.form["flag"]

View File

@ -6,6 +6,7 @@ cdn = True
proxied_ip_header = "X-Forwarded-For"
flag_rl = 10
teams_on_graph = 10
mail_from = "tjctf@sandbox1431.mailgun.org"
# Don't touch these. Instead, copy secrets.example to secrets and edit that.
import yaml

View File

@ -37,7 +37,7 @@ elif operation == "gen-team":
diff = timedelta(minutes=5)
for i in range(n):
name = "Team {}".format(i + 1)
t = Team.create(name=name, email="none@none.com", affiliation="Autogenerated", eligible=True, key="")
t = Team.create(name=name, email="none@none.com", affiliation="Autogenerated", eligible=True, key="", email_confirmation_key="autogen", email_confirmed=True)
t.key = "autogen{}".format(t.id)
t.save()
print("Team added with id {}".format(t.id))

View File

@ -11,6 +11,8 @@ class Team(BaseModel):
affiliation = CharField()
eligible = BooleanField()
first_login = BooleanField(default=True)
email_confirmed = BooleanField(default=False)
email_confirmation_key = CharField()
key = CharField()
def solved(self, challenge):

View File

@ -44,6 +44,26 @@
</div>
</div>
</div>
{% if not team.email_confirmed %}
<div class="row">
<div class="col s12">
<div class="card red darken-2">
<div class="card-content white-text">
<span class="card-title">Email unconfirmed</span>
<p>It looks like you haven't confirmed your email yet. Check the email you used for registration;
the system should have sent you an email confirmation key. Paste it below:</p>
<form action="{{ url_for('confirm_email') }}" method="POST">
<div class="input-field">
<label for="confirmation-key" class="white-text">Confirmation key</label>
<input required id="confirmation-key" name="confirmation_key" type="text" />
</div>
<button class="btn waves-effect waves-light" type="submit">Confirm email</button>
</form>
</div>
</div>
</div>
</div>
{% endif %}
<h4>Team information</h4>
<p>Your score is currently {{ team_score }}. <a href="{{ url_for('challenges') }}">Go solve more challenges!</a></p>
<p>Your team email is <code>{{ team.email }}</code>, and you are affiliated with

View File

@ -1,6 +1,7 @@
import random
import config
import json
import requests
from datetime import datetime
from functools import wraps
from flask import request, session, redirect, url_for, flash, g
@ -14,6 +15,9 @@ def generate_random_string(length=32, chars=allowed_chars):
def generate_team_key():
return config.ctf_name.lower() + "_" + generate_random_string(32, allowed_chars)
def generate_confirmation_key():
return generate_random_string(48)
def get_ip():
return request.headers.get(config.proxied_ip_header, request.remote_addr)
@ -26,6 +30,18 @@ def login_required(f):
return f(*args, **kwargs)
return decorated
def confirmed_email_required(f):
@wraps(f)
def decorated(*args, **kwargs):
if "team_id" not in session:
flash("You need to be logged in to access that page.")
return redirect(url_for('login'))
if not g.team.email_confirmed:
flash("You need to confirm your email in order to access that page.")
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated
def competition_running_required(f):
@wraps(f)
def decorated(*args, **kwargs):
@ -78,3 +94,20 @@ def get_complex(key):
def set_complex(key, val, ex):
g.redis.set(key, json.dumps(val), ex)
def send_email(to, subject, text):
return requests.post("{}/messages".format(config.secret.mailgun_url), {"from": config.mail_from, "to": to, "subject": subject, "text": text}, auth=("api", config.secret.mailgun_key))
def send_confirmation_email(team_email, confirmation_key, team_key):
send_email(team_email, "Welcome to {}!".format(config.ctf_name),
"""Hello, and thanks for registering for {}! Before you can start solving problems,
you must confirm your email by entering this code into the team dashboard:
{}
Once you've done that, your account will be enabled, and you will be able to access
the challenges. If you have any trouble, feel free to contact an organizer!
If you didn't register an account, then you can disregard this email.
In case you lose it, your team key is: {}""".format(config.ctf_name, confirmation_key, team_key))