Skip to content

Commit

Permalink
Fix contest ban/unban/hide/unhide functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jdabtieu committed Dec 24, 2023
1 parent b1d1048 commit a251f47
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 103 deletions.
19 changes: 9 additions & 10 deletions src/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from flask_mail import Message
from werkzeug.security import check_password_hash

from db import db


USER_PERM = {
"SUPERADMIN": 0,
Expand Down Expand Up @@ -78,7 +80,6 @@ def api_logged_in() -> bool:
return False

# Check API key
from application import db
user = db.execute("SELECT * FROM users WHERE api=?", sha256sum(request.args["key"]))
return len(user) == 1

Expand Down Expand Up @@ -113,7 +114,6 @@ def api_get_perms() -> set:
return set()

# Check API key
from application import db
user = db.execute("SELECT * FROM users WHERE api=?", sha256sum(request.args["key"]))
if len(user) == 0:
return set()
Expand Down Expand Up @@ -246,8 +246,7 @@ def create_jwt(data, secret_key, time=1800):
return jwt.encode(data, secret_key, algorithm='HS256')


def update_dyn_score(contest_id, problem_id, update_curr_user=True, transact=True):
from application import db
def update_dyn_score(contest_id, problem_id, update_curr_user=True, transact=True, d_solves=1):

Check failure on line 249 in src/helpers.py

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

E501 line too long (95 > 90 characters)

Check failure on line 249 in src/helpers.py

View workflow job for this annotation

GitHub Actions / build (windows-latest)

E501 line too long (95 > 90 characters)
"""
Updates the dynamic scoring of contest_id/problem_id, using the db object
For details see: https://www.desmos.com/calculator/eifeir81wk
Expand All @@ -261,14 +260,16 @@ def update_dyn_score(contest_id, problem_id, update_curr_user=True, transact=Tru
cid=contest_id, pid=problem_id, uid=session["user_id"])
check = db.execute(("SELECT * FROM contest_problems WHERE contest_id=:cid AND "
"problem_id=:pid"), cid=contest_id, pid=problem_id)
solves = len(db.execute(
"SELECT user_id FROM contest_solved WHERE contest_id=:cid AND problem_id=:pid",
cid=contest_id, pid=problem_id))
solves = db.execute(
("SELECT COUNT(user_id) AS cnt FROM contest_solved WHERE contest_id=? AND "
"problem_id=? AND user_id NOT IN (SELECT user_id FROM contest_users WHERE "
"contest_id=? AND hidden != 0)"),
contest_id, problem_id, contest_id)[0]["cnt"]
N_min = check[0]["score_min"]
N_max = check[0]["score_max"]
N_users = check[0]["score_users"]
d = 11 * math.log(N_max - N_min) + N_users
old_points = min(math.ceil(math.e**((d - solves + 1) / 11) + N_min), N_max)
old_points = min(math.ceil(math.e**((d - (solves - d_solves)) / 11) + N_min), N_max)
new_points = min(math.ceil(math.e**((d - solves) / 11) + N_min), N_max)
point_diff = new_points - old_points

Expand All @@ -293,7 +294,6 @@ def update_dyn_score(contest_id, problem_id, update_curr_user=True, transact=Tru


def contest_exists(contest_id):
from application import db
"""
Checks if the contest with contest_id exists
"""
Expand Down Expand Up @@ -367,7 +367,6 @@ def contest_ended(info):


def rejudge_contest_problem(contest_id, problem_id, new_flag):
from application import db
"""
Rejudges a contest problem
"""
Expand Down
2 changes: 1 addition & 1 deletion src/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ CREATE TABLE 'contest_users' (
'user_id' integer NOT NULL,
'points' integer NOT NULL DEFAULT (0),
'lastAC' datetime,
'hidden' integer NOT NULL DEFAULT(0),
'hidden' integer NOT NULL DEFAULT(0), -- 1: hidden, 2: banned
UNIQUE(contest_id, user_id) ON CONFLICT ABORT
);
CREATE TABLE 'contest_solved' (
Expand Down
61 changes: 43 additions & 18 deletions src/templates/contest/scoreboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ <h1><a href="{{ request.path[:-11] }}">{{ title }}</a></h1>
<td data-id="{{ data[i]['user_id'] }}" data-username="{{ data[i]['username'] }}">
<a href="#">
<img src="/assets/images/restrict.svg"
onerror="this.src='/assets/images/restrict.png'"
class="svg-red icon scoreboard-dq"
class="svg-red icon scoreboard-ban"
alt="Disqualify user"
title="Disqualify user">
</a>
Expand Down Expand Up @@ -80,27 +79,41 @@ <h1>Hidden Users</h1>
<tr>
<td>{{ i + 1 }}</td>
<td class="username">{{ hidden[i]["username"] }}</td>
<td>{{ hidden[i]["points"] }}</td>
<td class="{{ 'user-ban' if hidden[i]['hidden'] == 2 }}">{{ hidden[i]["points"] }}</td>
{% if hidden[i]["lastAC"] %}
<td class="dt">{{ hidden[i]["lastAC"] }}</td>
{% else %}
<td>None</td>
{% endif %}
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
<td data-id="{{ hidden[i]['user_id'] }}" data-username="{{ hidden[i]['username'] }}">
<a href="#">
<img src="/assets/images/restrict.svg"
onerror="this.src='/assets/images/restrict.png'"
class="svg-red icon scoreboard-dq"
alt="Disqualify user"
title="Disqualify user">
</a>
<a href="#">
<img src="/assets/images/ghost.svg"
class="icon scoreboard-unhide"
alt="Unhide user"
title="Unhide user">
</a>
{% if hidden[i]['hidden'] == 1 %}
<a href="#">
<img src="/assets/images/restrict.svg"
class="svg-red icon scoreboard-ban"
alt="Ban user"
title="Ban user">
</a>
<a href="#">
<img src="/assets/images/ghost.svg"
class="icon scoreboard-unhide"
alt="Unhide user"
title="Unhide user">
</a>
{% elif hidden[i]['hidden'] == 2 %}
<a href="#">
<img src="/assets/images/restrict.svg"
class="svg-red icon scoreboard-unban"
alt="Unban user"
title="Unban user">
</a>
<a href="#">
<img src="/assets/images/ghost.svg"
class="icon scoreboard-hide"
alt="Hide user"
title="Hide user">
</a>
{% endif %}
</td>
{% endif %}
</tr>
Expand All @@ -115,18 +128,30 @@ <h1>Hidden Users</h1>
<script>
const path = "{{ request.path }}";
const confirmDiv = document.getElementById("confirm");
for (let node of document.getElementsByClassName("scoreboard-dq")) {
for (let node of document.getElementsByClassName("scoreboard-ban")) {
node.parentElement.addEventListener("click", function() {
var username = this.parentElement.getAttribute("data-username");
var userId = this.parentElement.getAttribute("data-id");
confirmDiv.style.display = "block";
confirmDiv.querySelector(".btn")
.setAttribute("value", `Are you sure you want to disqualify ${username}? ` +
.setAttribute("value", `Are you sure you want to ban ${username}? ` +
"This action is irreversible. Click here to confirm");
confirmDiv.querySelector("input[name=user_id]").setAttribute("value", userId);
confirmDiv.querySelector("form").setAttribute("action", path + "/ban");
});
}
for (let node of document.getElementsByClassName("scoreboard-unban")) {
node.parentElement.addEventListener("click", function() {
var username = this.parentElement.getAttribute("data-username");
var userId = this.parentElement.getAttribute("data-id");
confirmDiv.style.display = "block";
confirmDiv.querySelector(".btn")
.setAttribute("value", `Are you sure you want to unban ${username}? ` +
"This action is irreversible. Click here to confirm");
confirmDiv.querySelector("input[name=user_id]").setAttribute("value", userId);
confirmDiv.querySelector("form").setAttribute("action", path + "/unban");
});
}
for (let node of document.getElementsByClassName("scoreboard-hide")) {
node.parentElement.addEventListener("click", function() {
var username = this.parentElement.getAttribute("data-username");
Expand Down
44 changes: 37 additions & 7 deletions src/tests/test_contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,15 @@ def test_contest(client, database):
}, follow_redirects=True)
assert result.status_code == 200
assert b'Hidden' not in result.data
assert b'unhidden' in result.data

result = client.get('/contest/testingcontest')
assert result.status_code == 200
assert b'1' in result.data # 1 non-hidden solves

client.post('/contest/testingcontest/scoreboard/hide', data={
'user_id': 1}, follow_redirects=True)
'user_id': 1
}, follow_redirects=True)

result = client.post('/contest/testingcontest/notify', data={
'subject': 'test subject',
Expand Down Expand Up @@ -252,6 +254,32 @@ def test_contest(client, database):
assert result.status_code == 200
assert b'Hidden' in result.data

result = client.post('/contest/testingcontest/scoreboard/ban', data={
'user_id': 2
}, follow_redirects=True)
assert result.status_code == 200
assert b'banned' in result.data
assert b'Hidden' in result.data

result = client.post('/contest/testingcontest/scoreboard/unban', data={
'user_id': 2
}, follow_redirects=True)
assert result.status_code == 200
assert b'unbanned' in result.data

result = client.post('/contest/testingcontest/scoreboard/hide', data={
'user_id': 2
}, follow_redirects=True)
assert result.status_code == 200
assert b'Hidden' in result.data

result = client.post('/contest/testingcontest/scoreboard/unhide', data={
'user_id': 1
}, follow_redirects=True)
assert result.status_code == 200
assert b'unhidden' in result.data
assert b'Hidden' in result.data

result = client.get('/admin/submissions')
assert result.status_code == 200
assert b'testingcontest-helloworldtesting' in result.data
Expand Down Expand Up @@ -285,10 +313,8 @@ def test_contest(client, database):

result = client.post('/contest/testingcontest/problem/dynscore', data={
'flag': 'ctf{hello}'
})
}, follow_redirects=True)
assert result.status_code == 200

result = client.get('/contest/testingcontest/problem/dynscore')
assert b'457' in result.data

result = client.post('/contest/testingcontest/problem/dynscore/edit', data={
Expand Down Expand Up @@ -316,6 +342,10 @@ def test_contest(client, database):
result = client.get('/contest/testingcontest/problem/dynscore')
assert b'500' in result.data

client.post('/contest/testingcontest/scoreboard/hide', data={
'user_id': 1
}, follow_redirects=True)

client.get('/logout')

client.post('/login', data={
Expand All @@ -337,7 +367,7 @@ def test_contest(client, database):

result = client.post('/contest/testingcontest/scoreboard/ban', follow_redirects=True, data={'user_id': 2})
assert result.status_code == 200
assert b'-999999' in result.data
assert b'user-ban' in result.data

result = client.get('/contest/testingcontest/problem/dynscore/download', follow_redirects=True)
assert result.status_code == 200
Expand Down Expand Up @@ -411,7 +441,7 @@ def test_contest_rejudge(client, database):
# WA --> First AC
result = client.post('/contest/testingcontest/problem/dynamic', data={
'flag': 'ctf{wrong}'
})
}, follow_redirects=True)
assert result.status_code == 200

result = client.get('/contest/testingcontest/scoreboard')
Expand Down Expand Up @@ -452,7 +482,7 @@ def test_contest_rejudge(client, database):
# WA --> AC and AC --> WA
result = client.post('/contest/testingcontest/problem/static', data={
'flag': 'ctf{hello}'
})
}, follow_redirects=True)
assert result.status_code == 200

result2 = client.get('/contest/testingcontest/scoreboard')
Expand Down
4 changes: 2 additions & 2 deletions src/tests/wintest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if [[ $(ls | grep wintest.sh | wc -c) -eq 0 ]]; then
fi
ls /tmp/CTFOJ_test 2>/dev/null
if [[ $? -eq 0 ]]; then
echo "/tmp/CTFOJ directory exists! Delete it for this test to run."
echo "/tmp/CTFOJ_test directory exists! Delete it for this test to run."
echo "**STOPPING**"
exit 1
fi
Expand All @@ -30,7 +30,7 @@ echo 'Setting up test environment...Done!'

# Run tests
echo 'Running tests...'
python -m pytest -v tests
python -m pytest -vv tests
echo 'Running tests...Done!'

# Cleanup
Expand Down
Loading

0 comments on commit a251f47

Please sign in to comment.