Re add view after ctf (#774)

* Re-add view_after_ctf configuration
* Add test for view_after_ctf functionality
selenium-screenshot-testing
Kevin Chung 2018-12-01 20:24:39 -05:00 committed by GitHub
parent 66c749fce6
commit 5a14cc2040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 16 deletions

View File

@ -10,7 +10,7 @@ from CTFd.utils.decorators import (
) )
from CTFd.utils.decorators.visibility import check_challenge_visibility from CTFd.utils.decorators.visibility import check_challenge_visibility
from CTFd.utils import config, text_type, user as current_user, get_config from CTFd.utils import config, text_type, user as current_user, get_config
from CTFd.utils.dates import ctftime, ctf_started, ctf_paused, ctf_ended, unix_time, unix_time_to_utc from CTFd.utils.dates import ctf_paused, view_after_ctf
from CTFd.utils.helpers import get_errors, get_infos from CTFd.utils.helpers import get_errors, get_infos
challenges = Blueprint('challenges', __name__) challenges = Blueprint('challenges', __name__)
@ -30,11 +30,7 @@ def listing():
if ctf_paused(): if ctf_paused():
infos.append('{} is paused'.format(config.ctf_name())) infos.append('{} is paused'.format(config.ctf_name()))
if not ctftime(): if view_after_ctf():
if ctf_started() is False: infos.append('{} has ended'.format(config.ctf_name()))
errors.append('{} has not started yet'.format(config.ctf_name()))
if ctf_ended():
errors.append('{} has ended'.format(config.ctf_name()))
return render_template('challenges.html', infos=infos, errors=errors, start=int(start), end=int(end))
return render_template('challenges.html', infos=infos, errors=errors, start=int(start), end=int(end)) return render_template('challenges.html', infos=infos, errors=errors, start=int(start), end=int(end))

View File

@ -2,15 +2,19 @@
<form method="POST" autocomplete="off" class="w-100"> <form method="POST" autocomplete="off" class="w-100">
<ul class="nav nav-tabs mb-3"> <ul class="nav nav-tabs mb-3">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="#start-date" aria-controls="start-date" role="tab" data-toggle="tab">Start <a class="nav-link active" href="#start-date" aria-controls="start-date" role="tab" data-toggle="tab">
Time</a> Start Time
</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#end-date" aria-controls="end-date" role="tab" data-toggle="tab">End Time</a> <a class="nav-link" href="#end-date" aria-controls="end-date" role="tab" data-toggle="tab">
End Time
</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#freeze-date" aria-controls="freeze-date" role="tab" data-toggle="tab">Freeze <a class="nav-link" href="#freeze-date" aria-controls="freeze-date" role="tab" data-toggle="tab">
Time</a> Freeze Time
</a>
</li> </li>
</ul> </ul>
@ -122,6 +126,14 @@
max="59" type='number'> max="59" type='number'>
</div> </div>
<div class="form-check">
<label>
<input id="view_after_ctf" name="view_after_ctf" type="checkbox"
{% if view_after_ctf %}checked{% endif %}>
Allow challenges to be viewed (no submissions are recorded) after the CTF End Time.
</label>
</div>
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<label for="end-timezone">Timezone:</label> <label for="end-timezone">Timezone:</label>
<select class="form-control end-date" id="end-timezone"> <select class="form-control end-date" id="end-timezone">

View File

@ -53,6 +53,10 @@ def ctf_ended():
return False return False
def view_after_ctf():
return get_config('view_after_ctf')
def unix_time(dt): def unix_time(dt):
return int((dt - datetime.datetime(1970, 1, 1)).total_seconds()) return int((dt - datetime.datetime(1970, 1, 1)).total_seconds())

View File

@ -1,7 +1,7 @@
from flask import request, redirect, url_for, session, abort, jsonify from flask import request, redirect, url_for, session, abort, jsonify
from CTFd.utils import config, get_config, get_app_config from CTFd.utils import config, get_config, get_app_config
from CTFd.cache import cache from CTFd.cache import cache
from CTFd.utils.dates import ctf_ended, ctf_paused, ctf_started, ctftime from CTFd.utils.dates import ctf_ended, ctf_paused, ctf_started, ctftime, view_after_ctf
from CTFd.utils import user as current_user from CTFd.utils import user as current_user
from CTFd.utils.user import get_current_user, get_current_team, is_admin, authed from CTFd.utils.user import get_current_user, get_current_team, is_admin, authed
from CTFd.utils.modes import TEAMS_MODE, USERS_MODE from CTFd.utils.modes import TEAMS_MODE, USERS_MODE
@ -21,9 +21,11 @@ def during_ctf_time_only(f):
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
if ctf_ended(): if ctf_ended():
if view_after_ctf():
return f(*args, **kwargs)
else:
error = '{} has ended'.format(config.ctf_name()) error = '{} has ended'.format(config.ctf_name())
abort(403, description=error) abort(403, description=error)
if ctf_started() is False: if ctf_started() is False:
error = '{} has not started yet'.format(config.ctf_name()) error = '{} has not started yet'.format(config.ctf_name())
abort(403, description=error) abort(403, description=error)

View File

@ -507,3 +507,78 @@ def test_challenges_cannot_be_solved_while_paused():
wrong_keys = Fails.query.count() wrong_keys = Fails.query.count()
assert wrong_keys == 0 assert wrong_keys == 0
destroy_ctfd(app) destroy_ctfd(app)
def test_challenges_under_view_after_ctf():
app = create_ctfd()
with app.app_context(), freeze_time("2017-10-7"):
set_config('start', '1507089600') # Wednesday, October 4, 2017 12:00:00 AM GMT-04:00 DST
set_config('end', '1507262400') # Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST
register_user(app)
client = login_as_user(app)
gen_challenge(app.db)
gen_flag(app.db, challenge_id=1, content='flag')
r = client.get('/challenges')
assert r.status_code == 403
r = client.get('/api/v1/challenges')
assert r.status_code == 403
assert r.get_json().get('data') is None
r = client.get('/api/v1/challenges/1')
assert r.status_code == 403
assert r.get_json().get('data') is None
data = {
"submission": 'flag',
"challenge_id": 1
}
r = client.post('/api/v1/challenges/attempt', json=data)
assert r.status_code == 403
assert r.get_json().get('data') is None
assert Solves.query.count() == 0
data = {
"submission": 'notflag',
"challenge_id": 1
}
r = client.post('/api/v1/challenges/attempt', json=data)
assert r.status_code == 403
assert r.get_json().get('data') is None
assert Fails.query.count() == 0
set_config('view_after_ctf', True)
r = client.get('/challenges')
assert r.status_code == 200
r = client.get('/api/v1/challenges')
assert r.status_code == 200
assert r.get_json()['data'][0]['id'] == 1
r = client.get('/api/v1/challenges/1')
assert r.status_code == 200
assert r.get_json()['data']['id'] == 1
data = {
"submission": 'flag',
"challenge_id": 1
}
r = client.post('/api/v1/challenges/attempt', json=data)
assert r.status_code == 200
assert r.get_json()['data']['status'] == "correct"
assert Solves.query.count() == 0
data = {
"submission": 'notflag',
"challenge_id": 1
}
r = client.post('/api/v1/challenges/attempt', json=data)
assert r.status_code == 200
assert r.get_json()['data']['status'] == "incorrect"
assert Fails.query.count() == 0
destroy_ctfd(app)