breakthrough points, challenge solves

master
Fox Wilson 2015-12-03 18:18:00 -05:00
parent 15b2b931a7
commit d90e5e54b6
8 changed files with 61 additions and 15 deletions

15
app.py
View File

@ -197,8 +197,17 @@ def dashboard():
def challenges():
chals = Challenge.select().order_by(Challenge.points)
solved = Challenge.select().join(ChallengeSolve).where(ChallengeSolve.team == g.team)
solves = {i: int(g.redis.hget("solves", i).decode()) for i in [k.id for k in chals]}
categories = sorted(list({chal.category for chal in chals}))
return render_template("challenges.html", challenges=chals, solved=solved, categories=categories)
return render_template("challenges.html", challenges=chals, solved=solved, categories=categories, solves=solves)
@app.route('/challenges/<int:challenge>/solves/')
@decorators.competition_running_required
@decorators.confirmed_email_required
def challenge_show_solves(challenge):
chal = Challenge.get(Challenge.id == challenge)
solves = ChallengeSolve.select(ChallengeSolve, Team).join(Team).order_by(ChallengeSolve.time).where(ChallengeSolve.challenge == chal)
return render_template("challenge_solves.html", challenge=chal, solves=solves)
@app.route('/submit/<int:challenge>/', methods=["POST"])
@decorators.competition_running_required
@ -291,10 +300,8 @@ def teardown_request(exc):
@app.before_request
def csrf_protect():
if app.debug:
return
if request.method == "POST":
token = session.pop('_csrf_token', None)
token = session.get('_csrf_token', None)
if not token or token != request.form["_csrf_token"]:
return "Invalid CSRF token!"

View File

@ -3,6 +3,7 @@ SUCCESS = (0, "Success!")
FLAG_SUBMISSION_TOO_FAST = (1001, "You're submitting flags too fast!")
FLAG_SUBMITTED_ALREADY = (1002, "You've already solved that problem!")
FLAG_INCORRECT = (1003, "Incorrect flag.")
FLAG_CANNOT_SUBMIT_WHILE_DISABLED = (1004, "You cannot submit a flag for a disabled problem.")
CAPTCHA_NOT_COMPLETED = (2001, "Please complete the CAPTCHA.")
CAPTCHA_INVALID = (2002, "Invalid CAPTCHA response.")

14
ctftool
View File

@ -8,10 +8,11 @@ import os
import os.path
import shutil
import sys
import yaml
import redis
import random
import utils
import utils.admin
import yaml
tables = [Team, TeamAccess, Challenge, ChallengeSolve, ChallengeFailure, NewsItem, TroubleTicket, TicketComment, Notification, ScoreAdjustment, AdminUser]
@ -52,11 +53,6 @@ elif operation == "gen-team":
t.save()
print("Team added with id {}".format(t.id))
for i in range(n * 10):
chal = random.choice(chals)
ctz -= diff
ChallengeSolve.create(team=Team.get(Team.id == (i % n) + 1), challenge=chal, time=ctz)
elif operation == "add-admin":
username = input("Username: ")
password = getpass.getpass().encode()
@ -104,4 +100,10 @@ elif operation == "scan":
print(n, "challenges loaded")
elif operation == "recache-solves":
r = redis.StrictRedis()
for chal in Challenge.select():
r.hset("solves", chal.id, chal.solves.count())
print(r.hvals("solves"))
# vim: syntax=python:ft=python

View File

@ -36,6 +36,8 @@ class Challenge(BaseModel):
author = CharField()
description = TextField()
points = IntegerField()
breakthrough_bonus = IntegerField(default=0)
enabled = BooleanField(default=True)
flag = CharField()
class ChallengeSolve(BaseModel):

View File

@ -70,7 +70,7 @@
<script>
$("abbr.time").timeago();
function dismissNotification(id) {
api.makeCall("/dismiss/" + id + ".json", {}, function() {
api.makeCall("/dismiss/" + id + ".json", {_csrf_token: "{{ csrf_token() }}"}, function() {
$("#notification" + id).slideUp();
});
}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<h2>Solves for {{ challenge.name }}</h2>
<a href="{{ url_for('challenges') }}">&lt;&lt; Back to challenges</a>
<table>
<thead>
<tr>
<th>Team</th>
<th>Solve time</th>
</tr>
</thead>
<tbody>
{% for solve in solves %}
<tr>
<td>{{ solve.team.name }}</td>
<td><abbr class="time" title="{{ solve.time }}">{{ solve.time }}</abbr></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -49,14 +49,19 @@ function filterCategories(t) {
<div id="header{{ challenge.id }}" class="collapsible-header{% if challenge not in solved %} active{% endif %}">
<strong style="font-size: 110%;">{{ challenge.name }}</strong>
<span class="left" style="margin-right: -5px;"><i id="check{{ challenge.id }}" style="display:{{ "block" if challenge in solved else "none" }}" class="material-icons">check</i></span>
<span class="right">{{ challenge.category }} <b>&middot;</b> {{ challenge.points }} pt</span>
<span class="right"><span id="solves{{ challenge.id }}">{{ solves[challenge.id] }}</span> solve(s) <b>&middot;</b> {{ challenge.category }} <b>&middot;</b> {{ challenge.points }} pt</span>
</div>
<div class="collapsible-body">
<p>{{ challenge.description | safe }}<br />
The challenge author is {{ challenge.author }}; talk to them for hints if you're stuck.</p>
The challenge author is {{ challenge.author }}; talk to them for hints if you're stuck.
{% if challenge in solved %}
<p>You've solved this challenge!</p>
<br /><br /><strong>You've solved this challenge!</strong><br />
<a href="{{ url_for('challenge_show_solves', challenge=challenge.id) }}">View solves</a>
</p>
{% else %}
<br /><br />
<a href="{{ url_for('challenge_show_solves', challenge=challenge.id) }}">View solves</a>
</p>
<form action="{{ url_for('submit', challenge=challenge.id) }}" data-challengeid="{{ challenge.id }}" method="POST">
<div class="container">
<div class="input-field">
@ -83,7 +88,7 @@ function filterCategories(t) {
<script>
$("form").submit(function(e) {
var id = $(this).attr("data-challengeid");
api.makeCall("/submit/" + id + ".json", {flag: $("#flag" + id).val()}, function(data) {
api.makeCall("/submit/" + id + ".json", {flag: $("#flag" + id).val(), _csrf_token: "{{ csrf_token() }}"}, function(data) {
if(data.code) {
Materialize.toast(data.message, 4000);
}
@ -91,6 +96,7 @@ function filterCategories(t) {
Materialize.toast("Flag accepted!", 4000);
$("#check" + id).show();
$("#header" + id).click();
$("#solves" + id).html(parseInt($("#solves" + id).html()) + 1);
}
});
return false;

View File

@ -10,10 +10,17 @@ def submit_flag(team, challenge, flag):
if team.solved(challenge):
return FLAG_SUBMITTED_ALREADY
elif not challenge.enabled:
return FLAG_CANNOT_SUBMIT_WHILE_DISABLED
elif challenge.flag != flag:
g.redis.set("rl{}".format(team.id), str(datetime.now()), config.flag_rl)
ChallengeFailure.create(team=team, challenge=challenge, attempt=flag, time=datetime.now())
return FLAG_INCORRECT
else:
if int(g.redis.hget("solves", challenge.id).decode()) == 0:
if challenge.breakthrough_bonus:
ScoreAdjustment.create(team=team, value=challenge.breakthrough_bonus, reason="First solve for {}".format(challenge.name))
g.redis.hincrby("solves", challenge.id, 1)
ChallengeSolve.create(team=team, challenge=challenge, time=datetime.now())
return SUCCESS