Merge branch 'master' of github.com:IceCTF/new-platform
commit
4c5f4cabf4
126
app.py
126
app.py
|
@ -5,7 +5,7 @@ from database import User, Team, TeamAccess, Challenge, ChallengeSolve, Challeng
|
|||
from datetime import datetime
|
||||
from peewee import fn
|
||||
|
||||
from utils import decorators, flag, cache, misc, captcha, email
|
||||
from utils import decorators, flag, cache, misc, captcha, email, select
|
||||
import utils.scoreboard
|
||||
|
||||
import config
|
||||
|
@ -28,11 +28,12 @@ def make_info_available():
|
|||
g.team = g.user.team
|
||||
g.team_restricts = g.team.restricts.split(",")
|
||||
except User.DoesNotExist:
|
||||
session.pop("user_id")
|
||||
return render_template("login.html")
|
||||
|
||||
@app.context_processor
|
||||
def scoreboard_variables():
|
||||
var = dict(config=config)
|
||||
var = dict(config=config, select=select)
|
||||
if "user_id" in session:
|
||||
var["logged_in"] = True
|
||||
var["user"] = g.user
|
||||
|
@ -108,10 +109,10 @@ def register():
|
|||
if request.method == "GET":
|
||||
return render_template("register.html")
|
||||
elif request.method == "POST":
|
||||
# error, message = captcha.verify_captcha()
|
||||
# if error:
|
||||
# flash(message)
|
||||
# return render_template("register.html")
|
||||
error, message = captcha.verify_captcha()
|
||||
if error:
|
||||
flash(message)
|
||||
return render_template("register.html")
|
||||
|
||||
username = request.form["username"].strip()
|
||||
user_email = request.form["email"].strip()
|
||||
|
@ -141,6 +142,10 @@ def register():
|
|||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
if password != confirm_password:
|
||||
flash("Password does not match confirmation")
|
||||
return render_template("register.html")
|
||||
|
||||
if not (user_email and "." in user_email and "@" in user_email):
|
||||
flash("You must have a valid email!")
|
||||
return render_template("register.html")
|
||||
|
@ -149,6 +154,18 @@ def register():
|
|||
flash("You're lying")
|
||||
return render_template("register.html")
|
||||
|
||||
if not tshirt_size in select.TShirts:
|
||||
flash("Invalid T-shirt size")
|
||||
return render_template("register.html")
|
||||
|
||||
if not background in select.BackgroundKeys:
|
||||
flash("Invalid Background")
|
||||
return render_template("register.html")
|
||||
|
||||
if not country in select.CountryKeys:
|
||||
flash("Invalid Background")
|
||||
return render_template("register.html")
|
||||
|
||||
confirmation_key = misc.generate_confirmation_key()
|
||||
|
||||
team=None
|
||||
|
@ -177,14 +194,15 @@ def register():
|
|||
email_confirmation_key=confirmation_key,
|
||||
team=team)
|
||||
user.setPassword(password)
|
||||
user.save()
|
||||
|
||||
print(confirmation_key)
|
||||
# print(confirmation_key)
|
||||
|
||||
# email.send_confirmation_email(user_email, confirmation_key)
|
||||
email.send_confirmation_email(user_email, confirmation_key)
|
||||
|
||||
session["user_id"] = user.id
|
||||
flash("Registration finished")
|
||||
return redirect(url_for('team_dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
@app.route('/logout/')
|
||||
def logout():
|
||||
|
@ -220,45 +238,73 @@ def user_dashboard():
|
|||
flash("You're changing your information too fast!")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
team_name = request.form["team_name"].strip()
|
||||
team_email = request.form["team_email"].strip()
|
||||
affiliation = request.form["affiliation"].strip()
|
||||
team_elig = "team_eligibility" in request.form
|
||||
username = request.form["username"].strip()
|
||||
user_email = request.form["email"].strip()
|
||||
password = request.form["password"].strip()
|
||||
confirm_password = request.form["confirm_password"].strip()
|
||||
background = request.form["background"].strip()
|
||||
country = request.form["country"].strip()
|
||||
|
||||
if len(team_name) > 50 or not team_name:
|
||||
flash("You must have a team name!")
|
||||
tshirt_size = request.form["tshirt_size"].strip()
|
||||
gender = request.form["gender"].strip()
|
||||
|
||||
if len(username) > 50 or not username:
|
||||
flash("You must have a username!")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
if g.user.username != username:
|
||||
try:
|
||||
user = User.get(User.username == username)
|
||||
flash("This username is already in use!")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
if not (user_email and "." in user_email and "@" in user_email):
|
||||
flash("You must have a valid email!")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
if not (team_email and "." in team_email and "@" in team_email):
|
||||
flash("You must have a valid team email!")
|
||||
if not email.is_valid_email(user_email):
|
||||
flash("You're lying")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
if not affiliation or len(affiliation) > 100:
|
||||
affiliation = "No affiliation"
|
||||
if not tshirt_size in select.TShirts:
|
||||
flash("Invalid T-shirt size")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
email_changed = (team_email != g.team.email)
|
||||
if not background in select.BackgroundKeys:
|
||||
flash("Invalid Background")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
g.team.name = team_name
|
||||
g.team.email = team_email
|
||||
g.team.affiliation = affiliation
|
||||
if not g.team.eligibility_locked:
|
||||
g.team.eligible = team_elig
|
||||
if not country in select.CountryKeys:
|
||||
flash("Invalid Background")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
email_changed = (user_email != g.user.email)
|
||||
|
||||
g.user.username = username
|
||||
g.user.email = user_email
|
||||
g.user.background = background
|
||||
g.user.country = country
|
||||
g.user.gender = gender
|
||||
g.user.tshirt_size = tshirt_size
|
||||
|
||||
g.redis.set("ul{}".format(session["user_id"]), str(datetime.now()), 120)
|
||||
|
||||
if email_changed:
|
||||
if not email.is_valid_email(team_email):
|
||||
flash("You're lying")
|
||||
if password != "":
|
||||
if password != confirm_password:
|
||||
flash("Password does not match confirmation")
|
||||
return redirect(url_for('user_dashboard'))
|
||||
g.user.setPassword(password)
|
||||
|
||||
g.team.email_confirmation_key = misc.generate_confirmation_key()
|
||||
g.team.email_confirmed = False
|
||||
if email_changed:
|
||||
g.user.email_confirmation_key = misc.generate_confirmation_key()
|
||||
g.user.email_confirmed = False
|
||||
|
||||
email.send_confirmation_email(team_email, g.team.email_confirmation_key, g.team.key)
|
||||
email.send_confirmation_email(user_email, g.user.email_confirmation_key)
|
||||
flash("Changes saved. Please check your email for a new confirmation key.")
|
||||
else:
|
||||
flash("Changes saved.")
|
||||
g.team.save()
|
||||
g.user.save()
|
||||
|
||||
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
@ -276,7 +322,7 @@ def team_dashboard():
|
|||
return redirect(url_for('team_dashboard'))
|
||||
|
||||
team_name = request.form["team_name"].strip()
|
||||
affiliation = request.form["affiliation"].strip()
|
||||
affiliation = request.form["team_affiliation"].strip()
|
||||
|
||||
if len(team_name) > 50 or not team_name:
|
||||
flash("You must have a team name!")
|
||||
|
@ -285,7 +331,13 @@ def team_dashboard():
|
|||
if not affiliation or len(affiliation) > 100:
|
||||
affiliation = "No affiliation"
|
||||
|
||||
email_changed = (team_email != g.team.email)
|
||||
if g.team_name != team_name:
|
||||
try:
|
||||
team = Team.get(Team.name == team_name)
|
||||
flash("This team name is already in use!")
|
||||
return redirect(url_for('team_dashboard'))
|
||||
except Team.DoesNotExist:
|
||||
pass
|
||||
|
||||
g.team.name = team_name
|
||||
g.team.affiliation = affiliation
|
||||
|
@ -433,13 +485,15 @@ def debug_app():
|
|||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
g.connected = True
|
||||
db.connect()
|
||||
g.redis = redis.StrictRedis()
|
||||
|
||||
@app.teardown_request
|
||||
def teardown_request(exc):
|
||||
db.close()
|
||||
g.redis.connection_pool.disconnect()
|
||||
if getattr(g, 'connected', False):
|
||||
db.close()
|
||||
g.redis.connection_pool.disconnect()
|
||||
|
||||
# CSRF things
|
||||
|
||||
|
|
8
ctftool
8
ctftool
|
@ -149,15 +149,15 @@ def main():
|
|||
subparser_problems = parser_problems.add_subparsers(help='Select one of the following actions')
|
||||
|
||||
parser_problems_scan = subparser_problems.add_parser('scan', help='Scan a path for problems')
|
||||
parser_problems_scan.add_argument("path", nargs=1, help="Directory for the path")
|
||||
parser_problems_scan.add_argument("path", help="Directory for the path")
|
||||
parser_problems_scan.set_defaults(func=scan_challenges)
|
||||
|
||||
parser_problems_generate = subparser_problems.add_parser('generate', help='Generate some dummy problems')
|
||||
parser_problems_generate.add_argument("challenge_count", nargs=1, type=int, help="number of problems to generate")
|
||||
parser_problems_generate.add_argument("challenge_count", type=int, help="number of problems to generate")
|
||||
parser_problems_generate.set_defaults(func=gen_challenges)
|
||||
|
||||
parser_problems_add = subparser_problems.add_parser('add', help='add a problem')
|
||||
parser_problems_add.add_argument("file", nargs=1, help="file to use")
|
||||
parser_problems_add.add_argument("file", help="file to use")
|
||||
parser_problems_add.set_defaults(func=add_challenge)
|
||||
|
||||
parser_problems_list = subparser_problems.add_parser('list', help='List problems in the database')
|
||||
|
@ -187,7 +187,7 @@ def main():
|
|||
subparser_users = parser_user.add_subparsers(help="Select one of the following actions")
|
||||
|
||||
parser_users_generate = subparser_users.add_parser('generate', help='Generate some dummy teams')
|
||||
parser_users_generate.add_argument("team_count", nargs=1, type=int, help="number of teams to generate")
|
||||
parser_users_generate.add_argument("team_count", type=int, help="number of teams to generate")
|
||||
parser_users_generate.set_defaults(func=gen_team)
|
||||
|
||||
parser_users_admin = subparser_users.add_parser('add-admin', help='Create a new administrator')
|
||||
|
|
|
@ -30,6 +30,7 @@ class User(BaseModel):
|
|||
email_confirmed = BooleanField(default=False)
|
||||
email_confirmation_key = CharField()
|
||||
password = CharField(null = True)
|
||||
background = CharField()
|
||||
country = CharField()
|
||||
tshirt_size = CharField(null = True)
|
||||
gender = CharField(null = True)
|
||||
|
@ -45,7 +46,7 @@ class User(BaseModel):
|
|||
return bcrypt.checkpw(pw.encode("utf-8"), self.password)
|
||||
|
||||
def eligible(self):
|
||||
return self.country == "Iceland"
|
||||
return self.country == "ISL"
|
||||
|
||||
class TeamAccess(BaseModel):
|
||||
team = ForeignKeyField(Team, related_name='accesses')
|
||||
|
|
|
@ -5,3 +5,4 @@ bcrypt
|
|||
redis
|
||||
pyyaml
|
||||
oath
|
||||
pycountry
|
||||
|
|
|
@ -75,17 +75,18 @@
|
|||
</a></li>
|
||||
<div class="divider" /></div>
|
||||
{% if logged_in %}
|
||||
<li><a href="{{ url_for('logout') }}">
|
||||
<div class="side-icon"><i class="material-icons red-text">block</i></div>
|
||||
<div class="side-text">Logout</div>
|
||||
|
||||
<li><a href="{{ url_for('team_dashboard') }}">
|
||||
<div class="side-icon"><i class="material-icons teal-text">track_changes</i></div>
|
||||
<div class="side-text">My Team</div>
|
||||
</a></li>
|
||||
<li><a href="{{ url_for('user_dashboard') }}">
|
||||
<div class="side-icon"><i class="material-icons teal-text">account_circle</i></div>
|
||||
<div class="side-text">{{ user.username }}</div>
|
||||
</a></li>
|
||||
<li><a href="{{ url_for('team_dashboard') }}">
|
||||
<div class="side-icon"><i class="material-icons teal-text">account_circle</i></div>
|
||||
<div class="side-text">{{ team.name }}</div>
|
||||
<li><a href="{{ url_for('logout') }}">
|
||||
<div class="side-icon"><i class="material-icons red-text">block</i></div>
|
||||
<div class="side-text">Logout</div>
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if not logged_in %}
|
||||
|
|
|
@ -39,20 +39,13 @@
|
|||
<div class="row no-bot">
|
||||
<div class="input-field col s6">
|
||||
<select required id="background" name="background">
|
||||
<option val="elementary">Elementary School Student</option>
|
||||
<option val="high">High School Student</option>
|
||||
<option val="university" selected>University Student</option>
|
||||
<option val="teacher">Teacher</option>
|
||||
<option val="professional">Security Professional</option>
|
||||
<option val="hobbyist">CTF Hobbyist(non-student)</option>
|
||||
<option val="other">Other</option>
|
||||
{{select.genoption(select.Backgrounds, selected="university")|safe}}
|
||||
</select>
|
||||
<label for="background">Background</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<select required name="country" id="country">
|
||||
<option value="" disabled selected>Choose your option</option>
|
||||
<option value="Iceland">Iceland</option>
|
||||
{{select.genoption(select.Countries, header='<option value="" disabled selected>Select country</option>')|safe}}
|
||||
</select>
|
||||
<label>Country</label>
|
||||
</div>
|
||||
|
@ -60,19 +53,14 @@
|
|||
<div class="row no-bot">
|
||||
<div class="input-field col s6">
|
||||
<select required name="tshirt_size" id="tshirt_size">
|
||||
<option value="" disabled selected>Choose your option</option>
|
||||
<option value="S">S</option>
|
||||
<option value="M">M</option>
|
||||
<option value="L">L</option>
|
||||
<option value="XL">XL</option>
|
||||
<option value="XXL">XXL</option>
|
||||
{{select.genoption(select.TShirts, header='<option value="" disabled selected>Select country</option>')|safe}}
|
||||
</select>
|
||||
<label>Shirt Size</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<input name="gender" type="radio" id="male" />
|
||||
<input name="gender" type="radio" id="male" value="M" />
|
||||
<label for="male">Male</label>
|
||||
<input name="gender" type="radio" id="female" />
|
||||
<input name="gender" type="radio" id="female" value="F" />
|
||||
<label for="female" style="margin-left: 1rem;">Female</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -31,75 +31,87 @@
|
|||
{% endblock %}
|
||||
{% block content %}
|
||||
<h2>{{ team.name }}</h2>
|
||||
|
||||
<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
|
||||
{{ team.affiliation }}.</p>
|
||||
<p>Your team is currently marked {{ "eligible" if team.eligible else "ineligible" }}.</p>
|
||||
<section>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card blue darken-1">
|
||||
<div class="card-content white-text">
|
||||
<span class="card-title">Team key: <code>{{ team.key }}</code></span>
|
||||
<p>Share this with your teammates, and keep it in a safe place.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Edit information</h4>
|
||||
<form method="POST">
|
||||
<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
|
||||
{{ team.affiliation }}.</p>
|
||||
<p>Your team is currently marked {{ "eligible" if team.eligible() else "ineligible" }}.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Edit information</h4>
|
||||
<form method="POST">
|
||||
<div class="input-field">
|
||||
<label for="team-name">Team Name</label>
|
||||
<input required maxlength="50" id="team-name" name="team_name" type="text" value="{{ team.name }}" />
|
||||
<label for="team-name">Team Name</label>
|
||||
<input required maxlength="50" id="team-name" name="team_name" type="text" value="{{ team.name }}" />
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input required maxlength="100" id="affiliation" name="affiliation" type="text" value="{{ team.affiliation }}" />
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input required maxlength="100" id="affiliation" name="team_affiliation" type="text" value="{{ team.affiliation }}" />
|
||||
</div>
|
||||
<input name="_csrf_token" type="hidden" value="{{ csrf_token() }}" />
|
||||
<br /><br />
|
||||
<button class="btn waves-effect waves-light" type="submit">Update team</button>
|
||||
</form>
|
||||
</form>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Score calculation</h4>
|
||||
{% if team_solves.count() %}
|
||||
<h5>Solved problems</h5>
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
<h4>Score calculation</h4>
|
||||
{% if team_solves.count() %}
|
||||
<h5>Solved problems</h5>
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Category</th><th>Time</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for solve in team_solves %}
|
||||
<tr>
|
||||
<td>{{ solve.challenge.name }}</td>
|
||||
<td>{{ solve.challenge.category }}</td>
|
||||
<td><abbr class="time" title="{{ solve.time }}">{{ solve.time }}</abbr></td>
|
||||
<td>{{ solve.challenge.points }}</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Category</th><th>Time</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for solve in team_solves %}
|
||||
<tr>
|
||||
<td>{{ solve.challenge.name }}</td>
|
||||
<td>{{ solve.challenge.category }}</td>
|
||||
<td><abbr class="time" title="{{ solve.time }}">{{ solve.time }}</abbr></td>
|
||||
<td>{{ solve.challenge.points }}</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No problems have been solved.</p>
|
||||
{% endif %}
|
||||
{% if team_adjustments.count() %}
|
||||
<h5>Score adjustments</h5>
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
{% else %}
|
||||
<p>No problems have been solved.</p>
|
||||
{% endif %}
|
||||
{% if team_adjustments.count() %}
|
||||
<h5>Score adjustments</h5>
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Reason</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for adj in team_adjustments %}
|
||||
<tr>
|
||||
<td>{{ adj.reason }}</td>
|
||||
<td>{{ adj.value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr><th>Reason</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for adj in team_adjustments %}
|
||||
<tr>
|
||||
<td>{{ adj.reason }}</td>
|
||||
<td>{{ adj.value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No score adjustments have been made.</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p>No score adjustments have been made.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -32,27 +32,94 @@
|
|||
{% block content %}
|
||||
<h2>{{ user.username }}</h2>
|
||||
{% if not user.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>
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}" />
|
||||
<button class="btn waves-effect waves-light" type="submit">Confirm email</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}" />
|
||||
<button class="btn waves-effect waves-light" type="submit">Confirm email</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<section>
|
||||
<h4>Edit information</h4>
|
||||
<form method="POST">
|
||||
<div class="row no-bot">
|
||||
<div class="input-field col s12">
|
||||
<input class="validate" required maxlength="50" id="username" name="username" type="text" value="{{ user.username }}" />
|
||||
<label for="team-name">Username</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-bot">
|
||||
<div class="input-field col s12">
|
||||
<input class="validate" required id="email" name="email" type="email" value="{{ user.email }}" />
|
||||
<label for="email">Email</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-bot">
|
||||
<div class="input-field col s6">
|
||||
<input class="validate" id="password" name="password" type="password" />
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<input class="validate" id="confirm_password" name="confirm_password" type="password" />
|
||||
<label for="confirm_password">Confirm Password</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-bot">
|
||||
<div class="input-field col s6">
|
||||
<select required id="background" name="background">
|
||||
{{select.genoption(select.Backgrounds, selected=user.background)|safe}}
|
||||
</select>
|
||||
<label for="background">Background</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<select required name="country" id="country">
|
||||
{{select.genoption(select.Countries, selected=user.country)|safe}}
|
||||
</select>
|
||||
<label>Country</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-bot">
|
||||
<div class="input-field col s6">
|
||||
<select required name="tshirt_size" id="tshirt_size">
|
||||
{{select.genoption(select.TShirts, selected=user.tshirt_size)|safe}}
|
||||
</select>
|
||||
<label>Shirt Size</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
{% if user.gender == 'M' %}
|
||||
<input name="gender" type="radio" id="male" value="M" checked="checked" />
|
||||
<label for="male">Male</label>
|
||||
<input name="gender" type="radio" id="female" value="F" />
|
||||
<label for="female" style="margin-left: 1rem;">Female</label>
|
||||
{% elif user.gender == 'F' %}
|
||||
<input name="gender" type="radio" id="male" value="M" />
|
||||
<label for="male">Male</label>
|
||||
<input name="gender" type="radio" id="female" value="F" checked="checked" />
|
||||
<label for="female" style="margin-left: 1rem;">Female</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-bot">
|
||||
<div class="col s12 m6">
|
||||
<button class="btn waves-effect waves-light right register-submit" type="submit">Update information</button>
|
||||
</div>
|
||||
</div>
|
||||
<input name="_csrf_token" type="hidden" value="{{ csrf_token() }}" />
|
||||
</form>
|
||||
</section>
|
||||
<div id="firstlogin" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Welcome to {{ config.ctf_name }}!</h4>
|
||||
|
|
|
@ -37,7 +37,7 @@ def calculate_scores():
|
|||
most_recent_solve = {tid: max([i.time for i in team_solves[tid]]) for tid in team_solves if team_solves[tid]}
|
||||
scores = {i: j for i, j in scores.items() if i in most_recent_solve}
|
||||
# eligible, teamid, teamname, affiliation, score
|
||||
return [(team_mapping[i[0]].eligible, i[0], team_mapping[i[0]].name, team_mapping[i[0]].affiliation, i[1]) for idx, i in enumerate(sorted(scores.items(), key=lambda k: (-k[1], most_recent_solve[k[0]])))]
|
||||
return [(team_mapping[i[0]].eligible(), i[0], team_mapping[i[0]].name, team_mapping[i[0]].affiliation, i[1]) for idx, i in enumerate(sorted(scores.items(), key=lambda k: (-k[1], most_recent_solve[k[0]])))]
|
||||
|
||||
def calculate_graph(scoredata):
|
||||
solves = list(ChallengeSolve.select(ChallengeSolve, Challenge).join(Challenge).order_by(ChallengeSolve.time))
|
||||
|
@ -55,4 +55,3 @@ def calculate_graph(scoredata):
|
|||
team_data.append((str(datetime.now()), score))
|
||||
graph_data.append((name, team_data))
|
||||
return graph_data
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import pycountry
|
||||
|
||||
TShirts = [
|
||||
"XS",
|
||||
"S",
|
||||
"M",
|
||||
"L",
|
||||
"XL",
|
||||
"XXL"
|
||||
]
|
||||
|
||||
Backgrounds = [
|
||||
("elementary", "Elementary School Student"),
|
||||
("high", "High School Student"),
|
||||
("university", "University Student"),
|
||||
("teacher", "Teacher"),
|
||||
("professional", "Security Professional"),
|
||||
("hobbyist", "CTF Hobbyist(non-student)"),
|
||||
("other", "Other")
|
||||
]
|
||||
|
||||
BackgroundKeys = [x[0] for x in Backgrounds]
|
||||
|
||||
Countries = [(country.alpha3, country.name) for country in pycountry.countries]
|
||||
Countries = (sorted(Countries, key=lambda x: "0" if x[1] == "Iceland" else x[1]))
|
||||
CountryKeys = [x[0] for x in Countries]
|
||||
|
||||
|
||||
def genoption(arr, selected=None, header=None):
|
||||
s = ""
|
||||
if header is not None:
|
||||
s += header
|
||||
for val in arr:
|
||||
if isinstance(val, tuple):
|
||||
val, name = val
|
||||
else:
|
||||
name = val
|
||||
if selected is not None and val == selected:
|
||||
s += ('<option value="%s" selected>%s</option>' % (val, name))
|
||||
else:
|
||||
s += ('<option value="%s">%s</option>' % (val, name))
|
||||
return s
|
Loading…
Reference in New Issue