breakthrough points, challenge solves
parent
15b2b931a7
commit
d90e5e54b6
15
app.py
15
app.py
|
@ -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!"
|
||||
|
||||
|
|
|
@ -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
14
ctftool
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>Solves for {{ challenge.name }}</h2>
|
||||
<a href="{{ url_for('challenges') }}"><< 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 %}
|
|
@ -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>·</b> {{ challenge.points }} pt</span>
|
||||
<span class="right"><span id="solves{{ challenge.id }}">{{ solves[challenge.id] }}</span> solve(s) <b>·</b> {{ challenge.category }} <b>·</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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue