mirror of https://github.com/JohnHammond/CTFd.git
Workshop mode (#477)
* Implementing workshop mode * Fixing a bug in /chals/solves where challenges with 0 solves weren't hidden. * Spinner errors are now 20vh down instead of 210px down * Users now use their private team endpoint at /team instead of /team/<id>selenium-screenshot-testing
parent
e34902c491
commit
18dd715276
|
@ -15,4 +15,4 @@ before_script:
|
|||
- psql -c 'create database ctfd;' -U postgres
|
||||
script:
|
||||
- pep8 --ignore E501,E712 CTFd/ tests/
|
||||
- nosetests
|
||||
- nosetests -d
|
||||
|
|
|
@ -117,6 +117,7 @@ def admin_config():
|
|||
mail_tls = bool(request.form.get('mail_tls', None))
|
||||
mail_ssl = bool(request.form.get('mail_ssl', None))
|
||||
mail_useauth = bool(request.form.get('mail_useauth', None))
|
||||
workshop_mode = bool(request.form.get('workshop_mode', None))
|
||||
except (ValueError, TypeError):
|
||||
view_challenges_unregistered = None
|
||||
view_scoreboard_if_authed = None
|
||||
|
@ -128,6 +129,7 @@ def admin_config():
|
|||
mail_tls = None
|
||||
mail_ssl = None
|
||||
mail_useauth = None
|
||||
workshop_mode = None
|
||||
finally:
|
||||
view_challenges_unregistered = utils.set_config('view_challenges_unregistered', view_challenges_unregistered)
|
||||
view_scoreboard_if_authed = utils.set_config('view_scoreboard_if_authed', view_scoreboard_if_authed)
|
||||
|
@ -139,6 +141,7 @@ def admin_config():
|
|||
mail_tls = utils.set_config('mail_tls', mail_tls)
|
||||
mail_ssl = utils.set_config('mail_ssl', mail_ssl)
|
||||
mail_useauth = utils.set_config('mail_useauth', mail_useauth)
|
||||
workshop_mode = utils.set_config('workshop_mode', workshop_mode)
|
||||
|
||||
mail_server = utils.set_config("mail_server", request.form.get('mail_server', None))
|
||||
mail_port = utils.set_config("mail_port", request.form.get('mail_port', None))
|
||||
|
@ -207,6 +210,8 @@ def admin_config():
|
|||
prevent_name_change = utils.get_config('prevent_name_change')
|
||||
verify_emails = utils.get_config('verify_emails')
|
||||
|
||||
workshop_mode = utils.get_config('workshop_mode')
|
||||
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
|
@ -236,4 +241,5 @@ def admin_config():
|
|||
prevent_name_change=prevent_name_change,
|
||||
verify_emails=verify_emails,
|
||||
view_after_ctf=view_after_ctf,
|
||||
themes=themes)
|
||||
themes=themes,
|
||||
workshop_mode=workshop_mode)
|
||||
|
|
|
@ -153,6 +153,11 @@ def solves_per_chal():
|
|||
if not utils.user_can_view_challenges():
|
||||
return redirect(url_for('auth.login', next=request.path))
|
||||
|
||||
chals = Challenges.query\
|
||||
.filter(or_(Challenges.hidden != True, Challenges.hidden == None))\
|
||||
.order_by(Challenges.value)\
|
||||
.all()
|
||||
|
||||
solves_sub = db.session.query(
|
||||
Solves.chalid,
|
||||
db.func.count(Solves.chalid).label('solves')
|
||||
|
@ -168,15 +173,21 @@ def solves_per_chal():
|
|||
) \
|
||||
.join(Challenges, solves_sub.columns.chalid == Challenges.id).all()
|
||||
|
||||
json = {}
|
||||
data = {}
|
||||
if utils.hide_scores():
|
||||
for chal, count, name in solves:
|
||||
json[chal] = -1
|
||||
data[chal] = -1
|
||||
for c in chals:
|
||||
if c.id not in data:
|
||||
data[c.id] = -1
|
||||
else:
|
||||
for chal, count, name in solves:
|
||||
json[chal] = count
|
||||
data[chal] = count
|
||||
for c in chals:
|
||||
if c.id not in data:
|
||||
data[c.id] = 0
|
||||
db.session.close()
|
||||
return jsonify(json)
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@challenges.route('/solves')
|
||||
|
|
|
@ -53,6 +53,16 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input id="workshop_mode" name="workshop_mode" type="checkbox"
|
||||
{% if workshop_mode %}checked{% endif %}>
|
||||
Workshop Mode <i class="fa fa-question-circle gray-text" data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
title="Hide all public team and score information."></i>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input id="hide_scores" name="hide_scores" type="checkbox" {% if hide_scores %}checked{% endif %}>
|
||||
|
|
|
@ -123,7 +123,7 @@ table{
|
|||
}
|
||||
|
||||
.spinner-error {
|
||||
padding-top: 210px;
|
||||
padding-top: 20vh;
|
||||
text-align: center;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
|
|
@ -53,8 +53,9 @@
|
|||
<li><a href="{{ request.script_root }}{{ menu.route }}">{{ menu.name }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if not get_config('workshop_mode') %}
|
||||
<li><a href="{{ request.script_root }}/teams">Teams</a></li>
|
||||
{% endif %}
|
||||
{% if not hide_scores() %}
|
||||
<li><a href="{{ request.script_root }}/scoreboard">Scoreboard</a></li>
|
||||
{% endif %}
|
||||
|
@ -65,7 +66,7 @@
|
|||
{% if admin %}
|
||||
<li><a href="{{ request.script_root }}/admin">Admin</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ request.script_root }}/team/{{ id }}">Team</a></li>
|
||||
<li><a href="{{ request.script_root }}/team">Team</a></li>
|
||||
<li><a href="{{ request.script_root }}/profile">Profile</a></li>
|
||||
<li><a href="{{ request.script_root }}/logout">Logout</a></li>
|
||||
{% else %}
|
||||
|
|
|
@ -217,7 +217,7 @@ def ctf_theme():
|
|||
|
||||
@cache.memoize()
|
||||
def hide_scores():
|
||||
return get_config('hide_scores')
|
||||
return get_config('hide_scores') or get_config('workshop_mode')
|
||||
|
||||
|
||||
def override_template(template, html):
|
||||
|
|
|
@ -120,6 +120,8 @@ def static_html(template):
|
|||
@views.route('/teams', defaults={'page': '1'})
|
||||
@views.route('/teams/<int:page>')
|
||||
def teams(page):
|
||||
if utils.get_config('workshop_mode'):
|
||||
abort(404)
|
||||
page = abs(int(page))
|
||||
results_per_page = 50
|
||||
page_start = results_per_page * (page - 1)
|
||||
|
@ -164,6 +166,9 @@ def private_team():
|
|||
|
||||
@views.route('/team/<int:teamid>', methods=['GET', 'POST'])
|
||||
def team(teamid):
|
||||
if utils.get_config('workshop_mode'):
|
||||
abort(404)
|
||||
|
||||
if utils.get_config('view_scoreboard_if_utils.authed') and not utils.authed():
|
||||
return redirect(url_for('auth.login', next=request.path))
|
||||
errors = []
|
||||
|
|
|
@ -56,7 +56,7 @@ def test_chals_solves():
|
|||
register_user(app, name=name, email=email, password="password")
|
||||
|
||||
# Generate 5 challenges
|
||||
for c in range(5):
|
||||
for c in range(6):
|
||||
chal1 = gen_challenge(app.db, value=100)
|
||||
|
||||
user_ids = list(range(2, 7))
|
||||
|
@ -76,7 +76,23 @@ def test_chals_solves():
|
|||
"2": 4,
|
||||
"3": 3,
|
||||
"4": 2,
|
||||
"5": 1
|
||||
"5": 1,
|
||||
"6": 0
|
||||
}
|
||||
''')
|
||||
received = json.loads(output)
|
||||
assert saved == received
|
||||
set_config('hide_scores', True)
|
||||
with client.session_transaction() as sess:
|
||||
r = client.get('/chals/solves')
|
||||
output = r.get_data(as_text=True)
|
||||
saved = json.loads('''{
|
||||
"1": -1,
|
||||
"2": -1,
|
||||
"3": -1,
|
||||
"4": -1,
|
||||
"5": -1,
|
||||
"6": -1
|
||||
}
|
||||
''')
|
||||
received = json.loads(output)
|
||||
|
@ -435,7 +451,10 @@ def test_that_view_challenges_unregistered_works():
|
|||
|
||||
r = client.get('/chals/solves')
|
||||
data = r.get_data(as_text=True)
|
||||
assert json.loads(data) == {}
|
||||
assert json.loads(data) == json.loads('''{
|
||||
"1": 0
|
||||
}
|
||||
''')
|
||||
|
||||
r = client.get('/chal/1/solves')
|
||||
data = r.get_data(as_text=True)
|
||||
|
|
|
@ -565,3 +565,53 @@ http://localhost/reset_password/InVzZXIxIi5BZktHUGcuTVhkTmZtOWU2U2xwSXZ1MlFwTjdw
|
|||
team = Teams.query.filter_by(email="user@user.com").first()
|
||||
assert team.password != team_password_saved
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_workshop_mode():
|
||||
"""Test that workshop mode hides the appropriate data"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
# Set CTFd to only allow confirmed users and send emails
|
||||
set_config('workshop_mode', True)
|
||||
|
||||
register_user(app)
|
||||
|
||||
chal = gen_challenge(app.db, value=100)
|
||||
solve = gen_solve(app.db, teamid=2, chalid=1)
|
||||
|
||||
client = login_as_user(app, name="user", password="password")
|
||||
r = client.get('/scoreboard')
|
||||
output = r.get_data(as_text=True)
|
||||
assert "Scores are currently hidden" in output
|
||||
|
||||
r = client.get('/scoreboard')
|
||||
output = r.get_data(as_text=True)
|
||||
assert "Scores are currently hidden" in output
|
||||
|
||||
r = client.get('/scores')
|
||||
received = r.get_data(as_text=True)
|
||||
saved = '''{
|
||||
"standings": []
|
||||
}
|
||||
'''
|
||||
assert json.loads(saved) == json.loads(received)
|
||||
|
||||
r = client.get('/teams')
|
||||
assert r.status_code == 404
|
||||
|
||||
r = client.get('/teams/1')
|
||||
assert r.status_code == 404
|
||||
|
||||
r = client.get('/teams/2')
|
||||
assert r.status_code == 404
|
||||
|
||||
r = client.get('/chals/solves')
|
||||
output = r.get_data(as_text=True)
|
||||
saved = json.loads('''{
|
||||
"1": -1
|
||||
}
|
||||
''')
|
||||
received = json.loads(output)
|
||||
assert saved == received
|
||||
|
||||
destroy_ctfd(app)
|
||||
|
|
Loading…
Reference in New Issue