Add a rough implementation of improving the challenge preview

This commit is contained in:
Kevin Chung
2023-06-26 19:27:27 -04:00
parent 89cec0c5e6
commit 79ad434d41
2 changed files with 253 additions and 0 deletions

View File

@@ -71,6 +71,50 @@ def challenges_detail(challenge_id):
)
from CTFd.utils.security.signing import serialize
from CTFd.utils.user import (
get_current_team,
get_current_user,
)
from CTFd.schemas.tags import TagSchema
@admin.route("/admin/challenges/preview/<int:challenge_id>")
@admins_only
def challenges_preview(challenge_id):
challenge = Challenges.query.filter_by(id=challenge_id).first_or_404()
chal_class = get_chal_class(challenge.type)
user = get_current_user()
team = get_current_team()
files = []
for f in challenge.files:
token = {
"user_id": user.id,
"team_id": team.id if team else None,
"file_id": f.id,
}
files.append(
url_for("views.files", path=f.location, token=serialize(token))
)
tags = [
tag["value"] for tag in TagSchema("user", many=True).dump(challenge.tags).data
]
content = render_template(
chal_class.templates["view"].lstrip("/"),
solves=None,
solved_by_me=False,
files=files,
tags=tags,
hints=challenge.hints,
max_attempts=challenge.max_attempts,
attempts=0,
challenge=challenge,
)
return render_template("admin/challenges/preview.html", content=content)
@admin.route("/admin/challenges/new")
@admins_only
def challenges_new():

View File

@@ -0,0 +1,209 @@
{% extends "page.html" %}
{% block content %}
{{ super() }}
<h4 style="text-align: center">
Preview may be slightly different from the user-facing implementation
</h4>
{% endblock %}
{% block scripts %}
{% if "beta" in Configs.ctf_theme %}
{{ Assets.js("assets/js/challenges.js") }}
{% else %}
<script type="module">
const displayHint = data => {
ezAlert({
title: "Hint",
body: data.html,
button: "Got it!"
});
};
const loadHint = id => {
CTFd.api.get_hint({ hintId: id, preview: true }).then(response => {
if (response.data.content) {
displayHint(response.data);
return;
}
// displayUnlock(id);
});
};
function renderSubmissionResponse(response) {
const result = response.data;
const result_message = $("#result-message");
const result_notification = $("#result-notification");
const answer_input = $("#challenge-input");
result_notification.removeClass();
result_message.text(result.message);
const next_btn = $(
`<div class='col-md-12 pb-3'><button class='btn btn-info w-100'>Next Challenge</button></div>`
).click(function () {
$("#challenge-window").modal("toggle");
setTimeout(function () {
loadChal(CTFd._internal.challenge.data.next_id);
}, 500);
});
if (result.status === "authentication_required") {
window.location =
CTFd.config.urlRoot +
"/login?next=" +
CTFd.config.urlRoot +
window.location.pathname +
window.location.hash;
return;
} else if (result.status === "incorrect") {
// Incorrect key
result_notification.addClass(
"alert alert-danger alert-dismissable text-center"
);
result_notification.slideDown();
answer_input.removeClass("correct");
answer_input.addClass("wrong");
setTimeout(function () {
answer_input.removeClass("wrong");
}, 3000);
} else if (result.status === "correct") {
// Challenge Solved
result_notification.addClass(
"alert alert-success alert-dismissable text-center"
);
result_notification.slideDown();
if (
$(".challenge-solves")
.text()
.trim()
) {
// Only try to increment solves if the text isn't hidden
$(".challenge-solves").text(
parseInt(
$(".challenge-solves")
.text()
.split(" ")[0]
) +
1 +
" Solves"
);
}
answer_input.val("");
answer_input.removeClass("wrong");
answer_input.addClass("correct");
if (CTFd._internal.challenge.data.next_id) {
$(".submit-row").html(next_btn);
}
} else if (result.status === "already_solved") {
// Challenge already solved
result_notification.addClass(
"alert alert-info alert-dismissable text-center"
);
result_notification.slideDown();
answer_input.addClass("correct");
if (CTFd._internal.challenge.data.next_id) {
$(".submit-row").html(next_btn);
}
} else if (result.status === "paused") {
// CTF is paused
result_notification.addClass(
"alert alert-warning alert-dismissable text-center"
);
result_notification.slideDown();
} else if (result.status === "ratelimited") {
// Keys per minute too high
result_notification.addClass(
"alert alert-warning alert-dismissable text-center"
);
result_notification.slideDown();
answer_input.addClass("too-fast");
setTimeout(function () {
answer_input.removeClass("too-fast");
}, 3000);
}
setTimeout(function () {
$(".alert").slideUp();
$("#challenge-submit").removeClass("disabled-button");
$("#challenge-submit").prop("disabled", false);
}, 3000);
}
CTFd._internal.challenge = {};
$.get(
CTFd.config.urlRoot + "/api/v1/challenges/1",
function (response) {
// Preview should not show any solves
var challenge_data = response.data;
challenge_data["solves"] = null;
$.getScript(
CTFd.config.urlRoot + challenge_data.type_data.scripts.view,
function () {
const challenge = CTFd._internal.challenge;
// Inject challenge data into the plugin
challenge.data = response.data;
challenge.preRender();
$("#challenge-input").addClass("form-control");
$("#challenge-submit").addClass(
"btn btn-md btn-outline-secondary float-right"
);
$(".challenge-solves").hide();
$(".nav-tabs a").click(function (e) {
e.preventDefault();
$(this).tab("show");
});
// Handle modal toggling
$("#challenge-window").on("hide.bs.modal", function (_event) {
$("#challenge-input").removeClass("wrong");
$("#challenge-input").removeClass("correct");
$("#incorrect-key").slideUp();
$("#correct-key").slideUp();
$("#already-solved").slideUp();
$("#too-fast").slideUp();
});
$(".load-hint").on("click", function (_event) {
loadHint($(this).data("hint-id"));
});
$("#challenge-submit").click(function (e) {
e.preventDefault();
$("#challenge-submit").addClass("disabled-button");
$("#challenge-submit").prop("disabled", true);
CTFd._internal.challenge
.submit(true)
.then(renderSubmissionResponse);
// Preview passed as true
});
$("#challenge-input").keyup(function (event) {
if (event.keyCode == 13) {
$("#challenge-submit").click();
}
});
challenge.postRender();
$("pre code")
.each(function (_idx) {
hljs.highlightBlock(this);
});
}
);
}
);
</script>
{% endif %}
{% endblock %}