Skip to content

Commit

Permalink
Add content manager role
Browse files Browse the repository at this point in the history
This role can edit all content, but not users, on the site.
#222
  • Loading branch information
jdabtieu committed Nov 27, 2023
1 parent b2113b0 commit 18cb167
Show file tree
Hide file tree
Showing 18 changed files with 89 additions and 70 deletions.
6 changes: 3 additions & 3 deletions src/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ def contests():


@app.route("/contests/create", methods=["GET", "POST"])
@admin_required
@perm_required(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])
def create_contest():
if request.method == "GET":
return render_template("contest/create.html")
Expand Down Expand Up @@ -688,7 +688,7 @@ def problems():


@app.route("/problems/create", methods=["GET", "POST"])
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"])
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"])
def create_problem():
if request.method == "GET":
return render_template("problem/create.html")
Expand Down Expand Up @@ -760,7 +760,7 @@ def create_problem():


@app.route('/problems/draft')
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"])
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"])
def draft_problems():
page = request.args.get("page")
if not page:
Expand Down
8 changes: 8 additions & 0 deletions src/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"SUPERADMIN": 0,
"ADMIN": 1,
"PROBLEM_MANAGER": 2,
"CONTENT_MANAGER": 3,
}


Expand Down Expand Up @@ -122,6 +123,13 @@ def api_admin() -> bool:
return check_perm(["ADMIN", "SUPERADMIN"], api_get_perms())


def api_perm(perms) -> bool:
"""
Check whether the API user matches any given permission, using API key or session
"""
return check_perm(perms, api_get_perms())


def login_required(f):
"""
Decorate routes to require login.
Expand Down
8 changes: 4 additions & 4 deletions src/templates/admin/console.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</div>
<h1>Admin Console</h1>
<div class="row">
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"]) %}
<div class="col-sm-6 admin-card">
<div class="card bg-ld-green">
<div class="card-body">
Expand All @@ -30,7 +30,7 @@ <h5 class="card-title">User Management</h5>
</div>
</div>
{% endif %}
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<div class="col-sm-6 admin-card">
<div class="card bg-ld-red">
<div class="card-body">
Expand All @@ -44,7 +44,7 @@ <h5 class="card-title">Create Announcements & Contests</h5>
</div>
</div>
{% endif %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"]) %}
<div class="col-sm-6 admin-card">
<div class="card bg-ld-purple">
<div class="card-body">
Expand All @@ -58,7 +58,7 @@ <h5 class="card-title">Manage Problems</h5>
</div>
</div>
{% endif %}
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<div class="col-sm-6 admin-card">
<div class="card bg-ld-blue">
<div class="card-body">
Expand Down
2 changes: 1 addition & 1 deletion src/templates/admin/submissions.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ <h3>Filter Submissions</h3>
placeholder="Filter by problem ID">
<label for="problem_id">Problem ID</label>
</div>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<div class="form-floating">
<input class="form-control mb-3"
id="contest_id"
Expand Down
12 changes: 7 additions & 5 deletions src/templates/contest/contest.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{% block main %}
<div style="position: relative; margin-bottom: 0.5rem;">
<h1 style="display: inline;">{{ title }}</h1>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<div class="dropdown" style="display: inline; margin-left: 4px;">
<button class="btn btn-secondary dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
Expand All @@ -18,10 +18,12 @@ <h1 style="display: inline;">{{ title }}</h1>
href="{{ request.path }}/addproblem">Add Problem</a>
<a class="dropdown-item"
href="{{ request.path }}/drafts">View Draft Problems</a>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
<a class="dropdown-item"
href="{{ request.path }}/notify">Notify Participants</a>
{% endif %}
<a class="dropdown-item"
href="{{ request.path }}/notify">Notify Participants</a>
<a class="dropdown-item"
href="/api/contest/scoreboard/{{ request.path[9:] }}?key={{ scoreboard_key }}">Export CTFtime Scoreboard</a>
href="/api/contest/scoreboard/{{ request.path[9:] }}?key={{ scoreboard_key }}">View CTFtime Scoreboard</a>
</div>
</div>
{% endif %}
Expand Down Expand Up @@ -62,7 +64,7 @@ <h1 style="display: inline;">{{ title }}</h1>
{% else %}
<td>{{ row["point_value"] }}</td>
{% endif %}
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<td><a href="/admin/submissions?problem_id={{ row['problem_id'] }}&contest_id={{ request.path[9:] }}&correct=AC">
{{ row["sols"] }}
</a></td>
Expand Down
6 changes: 3 additions & 3 deletions src/templates/contest/contest_problem.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h1>
{% endif %}
{{ data["name"] }}
</h1>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<div id="confirm" class="hidden">
<form method="post" style="margin-bottom: 1rem;" action="{{ request.path }}/publish">
<input class="btn btn-danger"
Expand Down Expand Up @@ -156,8 +156,8 @@ <h3 class="card-title">Live Instance</h3>
<div>
<b>Category:</b> {{ data["category"] }}<br>
<b>Points:</b> {{ data["point_value"] }}<br>
<a href="/contest/{{ request.path.split("/")[2] }}"><b>Back to contest</b></a>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
<a href="/contest/{{ request.path.split('/')[2] }}"><b>Back to contest</b></a>
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<br><a href="/admin/submissions?contest_id={{ request.path.split('/')[2] }}&problem_id={{ request.path.split('/')[4] }}">
View submissions
</a>
Expand Down
2 changes: 1 addition & 1 deletion src/templates/contest/contest_problem_noexist.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ <h1>Nonexistent problem</h1>
<div>
The problem with id {{ request.path.split("/")[4] }} does not exist.
</div>
<a href="/contest/{{ request.path.split("/")[2] }}">Back to contest</a>
<a href="/contest/{{ request.path.split('/')[2] }}">Back to contest</a>
{% endblock %}
6 changes: 3 additions & 3 deletions src/templates/contest/contests.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ <h3><a href="/contest/{{ contest['id'] }}">{{ contest["name"] }}</a></h3>
<p style="margin-bottom: 0;">Start: <span class="dt">{{ contest["start"] }}</span></p>
<p style="margin-bottom: 0;">End: <span class="dt">{{ contest["end"] }}</span></p>
<div id="contest-{{ contest['id'] }}" class="hidden smarkdown"></div>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<a href="/contest/{{ contest['id'] }}/edit"
style="position: absolute; top: 12px; right: 36px;">
<img src="/assets/images/pencil.svg"
Expand Down Expand Up @@ -49,7 +49,7 @@ <h3><a href="/contest/{{ contest['id'] }}">{{ contest["name"] }}</a></h3>
<p style="margin-bottom: 0;">Start: <span class="dt">{{ contest["start"] }}</span></p>
<p style="margin-bottom: 0;">End: <span class="dt">{{ contest["end"] }}</span></p>
<div id="contest-{{ contest['id'] }}" class="hidden smarkdown"></div>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<a href="/contest/{{ contest['id'] }}/edit"
style="position: absolute; top: 12px; right: 36px;">
<img src="/assets/images/pencil.svg"
Expand Down Expand Up @@ -81,7 +81,7 @@ <h3><a href="/contest/{{ contest['id'] }}">{{ contest["name"] }}</a></h3>
<p style="margin-bottom: 0;">Start: <span class="dt">{{ contest["start"] }}</span></p>
<p style="margin-bottom: 0;">End: <span class="dt">{{ contest["end"] }}</span></p>
<div id="contest-{{ contest['id'] }}" class="hidden smarkdown"></div>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<a href="/contest/{{ contest['id'] }}/edit"
style="position: absolute; top: 12px; right: 36px;">
<img src="/assets/images/pencil.svg"
Expand Down
4 changes: 2 additions & 2 deletions src/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

{% block main %}
<h1>Announcements</h1>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<div id="confirm" class="hidden">
<form method="post" style="margin-bottom: 1rem;" action="/admin/deleteannouncement">
<input class="btn btn-danger"
Expand All @@ -24,7 +24,7 @@ <h1>Announcements</h1>
<h3>{{ announcement["name"] }}</h3>
<p style="margin: 0;">Posted <span class="dt">{{ announcement["date"] }}</span></p>
<div id="announcement-{{ announcement['id'] }}" class="hidden smarkdown"></div>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<a href="/admin/editannouncement/{{ announcement['id'] }}"
style="position: absolute; top: 12px; right: 36px;">
<img src="/assets/images/pencil.svg"
Expand Down
2 changes: 1 addition & 1 deletion src/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<li><a class="nav-link" href="/ranking">Practice Leaderboard</a></li>
<li><a class="nav-link" href="/contests">Contests</a></li>
{% block contest_leaderboard %}{% endblock %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"]) %}
<li><a class="nav-link" href="/admin/console">Admin Console</a></li>
{% endif %}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion src/templates/problem/draft_problems.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h1>Draft Problems</h1>
<tbody>
{% for row in data %}
<tr>
<td><a href="/problem/{{ row["id"] }}">{{ row["name"] }}</a></td>
<td><a href="/problem/{{ row['id'] }}">{{ row["name"] }}</a></td>
<td>{{ row["category"] }}</td>
<td>{{ row["point_value"] }}</td>
</tr>
Expand Down
6 changes: 3 additions & 3 deletions src/templates/problem/problem.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h1>
{% endif %}
{{ data["name"] }}
</h1>
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"]) %}
<div id="confirm" style="display: none;">
<form method="post" style="margin-bottom: 1rem;" action="">
<input class="btn btn-danger" type="submit" value="">
Expand Down Expand Up @@ -157,7 +157,7 @@ <h3 class="card-title">Live Instance</h3>
{% if data["editorial"] %}
<br><a href="{{ request.path }}/editorial">View editorial</a>
{% endif %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"]) %}
<br><a href="/admin/submissions?problem_id={{ request.path.split('/')[2] }}">
View submissions
</a>
Expand Down Expand Up @@ -195,7 +195,7 @@ <h3 class="card-title">Live Instance</h3>
document.getElementById("hint").classList.toggle("hidden");
});
</script>
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"]) %}
{% if check_perm(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"]) %}
<script>
document.querySelector(".btn-delete").addEventListener("click", function () {
document.getElementById("confirm").style.display = "";
Expand Down
4 changes: 2 additions & 2 deletions src/templates/problem/problems.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ <h1>Problems</h1>
<td><a href="/problem/{{ row["id"] }}">{{ row["name"] }}</a></td>
<td>{{ row["category"] }}</td>
<td>{{ row["point_value"] }}</td>
{% if check_perm(["ADMIN", "SUPERADMIN"]) %}
<td><a href="/admin/submissions?problem_id={{ row["id"] }}&correct=AC">
{% if check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"]) %}
<td><a href="/admin/submissions?problem_id={{ row['id'] }}&correct=AC">
{{ row["sols"] }}
</a></td>
{% else %}
Expand Down
2 changes: 1 addition & 1 deletion src/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_admin(client, database):

result = client.post('/admin/updateperms?user_id=2', follow_redirects=True)
assert result.status_code == 200
assert b"revoked [&#39;ADMIN&#39;]" in result.data
assert b"Revoked [&#39;ADMIN&#39;]" in result.data

# Test announcements creation and editing
result = client.post('/admin/createannouncement', data={
Expand Down
32 changes: 17 additions & 15 deletions src/views/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,21 @@


@api.route("/console")
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"])
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"])
def admin_console():
return render_template("admin/console.html", ver="v4.1.1",
maintenance_mode=os.path.exists('maintenance_mode'))


@api.route("/submissions")
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER"])
@perm_required(["ADMIN", "SUPERADMIN", "PROBLEM_MANAGER", "CONTENT_MANAGER"])
def admin_submissions():
submissions = None

query = request.args
modifier = " WHERE"
args = []

# Permission-based overrides
query = request.args.copy()
if not check_perm(["ADMIN", "SUPERADMIN"]):
if check_perm(["PROBLEM_MANAGER"]):
query["contest_id"] = "None"

# Construct query
if query.get("username"):
modifier += " username=? AND"
Expand All @@ -43,7 +38,8 @@ def admin_submissions():
args.append(query.get("problem_id"))

if query.get("contest_id"):
if query.get("contest_id") == "None":
if (query.get("contest_id") == "None" or

Check warning on line 41 in src/views/admin.py

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

W504 line break after binary operator

Check warning on line 41 in src/views/admin.py

View workflow job for this annotation

GitHub Actions / build (windows-latest)

W504 line break after binary operator
not check_perm(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])):
modifier += " contest_id IS NULL AND"
else:
modifier += " contest_id=? AND"
Expand Down Expand Up @@ -186,6 +182,9 @@ def update_perms():
# Calculate old/new perms
perms_add = new_perms - cur_perms
perms_remove = cur_perms - new_perms
if not perms_add and not perms_remove:
flash("No perms were updated", "warning")
return redirect("/admin/users")

# Get friendly names
inv_perms = {v: k for k, v in USER_PERM.items()}
Expand All @@ -209,15 +208,18 @@ def update_perms():

# Flash and log message
msg = f"Permissions changed for user #{user_id} ({user[0]['username']}). "
msg += f"Granted {friendly_perms_add}, revoked {friendly_perms_remove}."
if friendly_perms_add:
msg += f"Granted {friendly_perms_add}. "
if friendly_perms_remove:
msg += f"Revoked {friendly_perms_remove}. "
flash(msg, "success")
logger.info(msg + f" Performed by user #{session['user_id']} ({session['username']})",
extra={"section": "auth"})
return redirect("/admin/users")


@api.route("/createannouncement", methods=["GET", "POST"])
@admin_required
@perm_required(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])
def createannouncement():
if request.method == "GET":
return render_template("admin/createannouncement.html")
Expand All @@ -244,7 +246,7 @@ def createannouncement():


@api.route("/deleteannouncement", methods=["POST"])
@admin_required
@perm_required(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])
def delete_announcement():
aid = request.form.get("aid")
if not aid:
Expand All @@ -260,7 +262,7 @@ def delete_announcement():


@api.route("/editannouncement/<aid>", methods=["GET", "POST"])
@admin_required
@perm_required(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])
def editannouncement(aid):
data = db.execute("SELECT * FROM announcements WHERE id=:aid", aid=aid)

Expand Down Expand Up @@ -316,7 +318,7 @@ def maintenance():


@api.route("/edithomepage", methods=["GET", "POST"])
@admin_required
@perm_required(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])
def edit_homepage():
if request.method == "GET":
return render_template("admin/edithomepage.html")
Expand All @@ -343,7 +345,7 @@ def edit_homepage():


@api.route("/previewhomepage")
@admin_required
@perm_required(["ADMIN", "SUPERADMIN", "CONTENT_MANAGER"])
def preview_homepage():
page = request.args.get("page")
if not page:
Expand Down
Loading

2 comments on commit 18cb167

@fireheartjerry
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

draft_problems.html has a weird change.

@jdabtieu
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

draft_problems.html has a weird change.

Oh that was my autoformatting

Please sign in to comment.