Merge branch 'master' into 3.1.0-dev

This commit is contained in:
Kevin Chung
2020-08-31 16:56:35 -04:00
13 changed files with 119 additions and 71 deletions

View File

@@ -1,3 +1,14 @@
# 3.0.2 / 2020-08-23
**Admin Panel**
- Fix submission searching in Admin Panel
- Fix update banner being hidden behind navbar
**Plugins**
- Change default `input` & `submit` blocks in `challenge.html` to use the default values specified in the original challenge type plugins
# 3.0.1 / 2020-08-12
**General**

View File

@@ -26,7 +26,7 @@ from CTFd.utils.migrations import create_database, migrations, stamp_latest_revi
from CTFd.utils.sessions import CachingSessionInterface
from CTFd.utils.updates import update_check
__version__ = "3.0.1"
__version__ = "3.0.2"
__channel__ = "oss"

View File

@@ -100,8 +100,7 @@ class ServerConfig(object):
CACHE_THRESHOLD: int = 0
# === SECURITY ===
SESSION_COOKIE_HTTPONLY: bool = config_ini["security"].getboolean("SESSION_COOKIE_HTTPONLY") \
or True
SESSION_COOKIE_HTTPONLY: bool = config_ini["security"].getboolean("SESSION_COOKIE_HTTPONLY", fallback=True)
SESSION_COOKIE_SAMESITE: str = empty_str_cast(config_ini["security"]["SESSION_COOKIE_SAMESITE"]) \
or "Lax"
@@ -173,38 +172,28 @@ class ServerConfig(object):
AWS_S3_ENDPOINT_URL: str = empty_str_cast(config_ini["uploads"]["AWS_S3_ENDPOINT_URL"])
# === OPTIONAL ===
REVERSE_PROXY: bool = empty_str_cast(config_ini["optional"]["REVERSE_PROXY"]) \
or False
REVERSE_PROXY: bool = empty_str_cast(config_ini["optional"]["REVERSE_PROXY"], default=False)
TEMPLATES_AUTO_RELOAD: bool = empty_str_cast(config_ini["optional"]["TEMPLATES_AUTO_RELOAD"]) \
or True
TEMPLATES_AUTO_RELOAD: bool = empty_str_cast(config_ini["optional"]["TEMPLATES_AUTO_RELOAD"], default=True)
SQLALCHEMY_TRACK_MODIFICATIONS: bool = empty_str_cast(config_ini["optional"]["SQLALCHEMY_TRACK_MODIFICATIONS"]) \
or False
SQLALCHEMY_TRACK_MODIFICATIONS: bool = empty_str_cast(config_ini["optional"]["SQLALCHEMY_TRACK_MODIFICATIONS"], default=False)
SWAGGER_UI: bool = empty_str_cast(config_ini["optional"]["SWAGGER_UI"]) \
or False
SWAGGER_UI: bool = empty_str_cast(config_ini["optional"]["SWAGGER_UI"], default=False)
SWAGGER_UI_ENDPOINT: str = "/" if SWAGGER_UI else None
UPDATE_CHECK: bool = empty_str_cast(config_ini["optional"]["UPDATE_CHECK"]) \
or True
UPDATE_CHECK: bool = empty_str_cast(config_ini["optional"]["UPDATE_CHECK"], default=True)
APPLICATION_ROOT: str = empty_str_cast(config_ini["optional"]["APPLICATION_ROOT"]) \
or "/"
APPLICATION_ROOT: str = empty_str_cast(config_ini["optional"]["APPLICATION_ROOT"], default="/")
SERVER_SENT_EVENTS: bool = empty_str_cast(config_ini["optional"]["SERVER_SENT_EVENTS"]) \
or True
SERVER_SENT_EVENTS: bool = empty_str_cast(config_ini["optional"]["SERVER_SENT_EVENTS"], default=True)
HTML_SANITIZATION: bool = empty_str_cast(config_ini["optional"]["HTML_SANITIZATION"]) \
or False
HTML_SANITIZATION: bool = empty_str_cast(config_ini["optional"]["HTML_SANITIZATION"], default=False)
if DATABASE_URL.startswith("sqlite") is False:
SQLALCHEMY_ENGINE_OPTIONS = {
"max_overflow": int(empty_str_cast(config_ini["optional"]["SQLALCHEMY_MAX_OVERFLOW"], default=0)) # noqa: E131
or 20, # noqa: E131
"pool_pre_ping": empty_str_cast(config_ini["optional"]["SQLALCHEMY_POOL_PRE_PING"]) # noqa: E131
or True, # noqa: E131
"max_overflow": int(empty_str_cast(config_ini["optional"]["SQLALCHEMY_MAX_OVERFLOW"], default=20)), # noqa: E131
"pool_pre_ping": empty_str_cast(config_ini["optional"]["SQLALCHEMY_POOL_PRE_PING"], default=True), # noqa: E131
}
# === OAUTH ===

View File

@@ -109,20 +109,18 @@
</div>
</nav>
{% if get_config('version_latest') %}
<div class="container-fluid bg-warning text-center py-3">
<div class="row">
<div class="col-md-12">
<a class="btn btn-warning" href="{{ get_config('version_latest') }}">
A new CTFd version is available!
</a>
<main role="main">
{% if get_config('version_latest') %}
<div class="container-fluid bg-warning text-center py-3">
<div class="row">
<div class="col-md-12">
<a class="btn btn-warning" href="{{ get_config('version_latest') }}">
A new CTFd version is available!
</a>
</div>
</div>
</div>
</div>
{% endif %}
<main role="main">
{% endif %}
{% block content %}
{% endblock %}
</main>

View File

@@ -29,30 +29,30 @@
<div class="form-group col-md-2">
<label for="start-month">Month:</label>
<input class="form-control start-date" id='start-month' name='start-month' min="0" max="12"
<input class="form-control start-date" id='start-month' min="0" max="12"
type='number'>
</div>
<div class="form-group col-md-2">
<label for="start-day">Day:</label>
<input class="form-control start-date" id='start-day' name='start-day' min="0" max="31"
<input class="form-control start-date" id='start-day' min="0" max="31"
type='number'>
</div>
<div class="form-group col-md-3">
<label for="start-year">Year:</label>
<input class="form-control start-date" id='start-year' name='start-year' type='number'>
<input class="form-control start-date" id='start-year' type='number'>
</div>
<div class="form-group col-md-2">
<label for="start-hour">Hour:</label>
<input class="form-control start-date" id='start-hour' name='start-hour' min="0" max="23"
<input class="form-control start-date" id='start-hour' min="0" max="23"
type='number'>
</div>
<div class="form-group col-md-3">
<label for="start-minute">Minute:</label>
<input class="form-control start-date" id='start-minute' name='start-minute' min="0" max="59"
<input class="form-control start-date" id='start-minute' min="0" max="59"
type='number'>
</div>
@@ -76,7 +76,7 @@
<div class="form-group col-md-12">
<label>UTC Timestamp:</label>
<input class="form-control" id='start' name='start' type='text'
<input class="form-control" id='start' name='start' type='number'
placeholder="Start Date (UTC timestamp)"
{% if start is defined and start != None %}value="{{ start }}"{% endif %} readonly>
</div>
@@ -92,30 +92,30 @@
<div class="form-group col-md-2">
<label for="end-month">Month:</label>
<input class="form-control end-date" id='end-month' name='end-month' min="0" max="12"
<input class="form-control end-date" id='end-month' min="0" max="12"
type='number'>
</div>
<div class="form-group col-md-2">
<label for="end-day">Day:</label>
<input class="form-control end-date" id='end-day' name='end-day' min="0" max="31"
<input class="form-control end-date" id='end-day' min="0" max="31"
type='number'>
</div>
<div class="form-group col-md-4">
<label for="end-year">Year:</label>
<input class="form-control end-date" id='end-year' name='end-year' type='number'>
<input class="form-control end-date" id='end-year' type='number'>
</div>
<div class="form-group col-md-2">
<label for="end-hour">Hour:</label>
<input class="form-control end-date" id='end-hour' name='end-hour' min="0" max="23"
<input class="form-control end-date" id='end-hour' min="0" max="23"
type='number'>
</div>
<div class="form-group col-md-2">
<label for="end-minute">Minute:</label>
<input class="form-control end-date" id='end-minute' name='end-minute' min="0"
<input class="form-control end-date" id='end-minute' min="0"
max="59" type='number'>
</div>
@@ -147,7 +147,7 @@
<div class="form-group col-md-12">
<label for="end">UTC Timestamp:</label>
<input class="form-control" id='end' name='end' type='text'
<input class="form-control" id='end' name='end' type='number'
placeholder="End Date (UTC timestamp)"
{% if end is defined and end != None %}value="{{ end }}"{% endif %} readonly>
</div>
@@ -164,30 +164,30 @@
<div class="form-group col-md-2">
<label for="freeze-month">Month:</label>
<input class="form-control freeze-date" id='freeze-month' name='freeze-month' min="0" max="12"
<input class="form-control freeze-date" id='freeze-month' min="0" max="12"
type='number'>
</div>
<div class="form-group col-md-2">
<label for="freeze-day">Day:</label>
<input class="form-control freeze-date" id='freeze-day' name='freeze-day' min="0" max="31"
<input class="form-control freeze-date" id='freeze-day' min="0" max="31"
type='number'>
</div>
<div class="form-group col-md-4">
<label for="freeze-year">Year:</label>
<input class="form-control freeze-date" id='freeze-year' name='freeze-year' type='number'>
<input class="form-control freeze-date" id='freeze-year' type='number'>
</div>
<div class="form-group col-md-2">
<label for="freeze-hour">Hour:</label>
<input class="form-control freeze-date" id='freeze-hour' name='freeze-hour' min="0" max="23"
<input class="form-control freeze-date" id='freeze-hour' min="0" max="23"
type='number'>
</div>
<div class="form-group col-md-2">
<label for="freeze-minute">Minute:</label>
<input class="form-control freeze-date" id='freeze-minute' name='freeze-minute' min="0"
<input class="form-control freeze-date" id='freeze-minute' min="0"
max="59" type='number'>
</div>
@@ -211,7 +211,7 @@
<div class="form-group col-md-12">
<label for="freeze">UTC Timestamp:</label>
<input class="form-control" id='freeze' name='freeze' type='text'
<input class="form-control" id='freeze' name='freeze' type='number'
placeholder="Freeze Date (UTC timestamp)"
{% if freeze is defined and freeze != None %}value="{{ freeze }}"{% endif %} readonly>
</div>

View File

@@ -30,16 +30,21 @@ function switchTab(event) {
}
function processDateTime(datetime) {
let date_picker = $(`#${datetime}-date`);
let time_picker = $(`#${datetime}-time`);
return function(_event) {
let date_picker = $(`#${datetime}-date`);
let time_picker = $(`#${datetime}-time`);
let unix_time = Moment(
`${date_picker.val()} ${time_picker.val()}`,
"YYYY-MM-DD HH:mm"
)
.utc()
.format("X");
$(`#${datetime}-preview`).val(unix_time);
.unix();
if (isNaN(unix_time)) {
$(`#${datetime}-preview`).val("");
} else {
$(`#${datetime}-preview`).val(unix_time);
}
};
}

View File

@@ -162,7 +162,7 @@
/***/ (function(module, exports, __webpack_require__) {
;
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/core/assets/js/pages/main.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _momentTimezone = _interopRequireDefault(__webpack_require__(/*! moment-timezone */ \"./node_modules/moment-timezone/index.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! ../CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction switchTab(event) {\n event.preventDefault(); // Handle tab validation\n\n var valid_tab = true;\n (0, _jquery.default)(event.target).closest(\"[role=tabpanel]\").find(\"input,textarea\").each(function (i, e) {\n var $e = (0, _jquery.default)(e);\n var status = e.checkValidity();\n\n if (status === false) {\n $e.removeClass(\"input-filled-valid\");\n $e.addClass(\"input-filled-invalid\");\n valid_tab = false;\n }\n });\n\n if (valid_tab == false) {\n return;\n }\n\n var href = (0, _jquery.default)(event.target).data(\"href\");\n (0, _jquery.default)(\".nav a[href=\\\"\".concat(href, \"\\\"]\")).tab(\"show\");\n}\n\nfunction processDateTime(datetime) {\n var date_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-date\"));\n var time_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-time\"));\n return function (_event) {\n var unix_time = (0, _momentTimezone.default)(\"\".concat(date_picker.val(), \" \").concat(time_picker.val()), \"YYYY-MM-DD HH:mm\").utc().format(\"X\");\n (0, _jquery.default)(\"#\".concat(datetime, \"-preview\")).val(unix_time);\n };\n}\n\nfunction mlcSetup(_event) {\n var params = {\n name: (0, _jquery.default)(\"#ctf_name\").val(),\n type: \"jeopardy\",\n description: (0, _jquery.default)(\"#ctf_description\").val(),\n user_mode: (0, _jquery.default)(\"#user_mode\").val(),\n event_url: window.location.origin + _CTFd.default.config.urlRoot,\n redirect_url: window.location.origin + _CTFd.default.config.urlRoot + \"/redirect\",\n integration_setup_url: window.location.origin + _CTFd.default.config.urlRoot + \"/setup/integrations\",\n start: (0, _jquery.default)(\"#start-preview\").val(),\n end: (0, _jquery.default)(\"#end-preview\").val(),\n platform: \"CTFd\",\n state: window.STATE\n };\n var ret = [];\n\n for (var p in params) {\n ret.push(encodeURIComponent(p) + \"=\" + encodeURIComponent(params[p]));\n }\n\n window.open(\"https://www.majorleaguecyber.org/events/new?\" + ret.join(\"&\"), \"_blank\");\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".tab-next\").click(switchTab);\n (0, _jquery.default)(\"input\").on(\"keypress\", function (e) {\n // Hook Enter button\n if (e.keyCode == 13) {\n e.preventDefault();\n (0, _jquery.default)(e.target).closest(\".tab-pane\").find(\"button[data-href]\").click();\n }\n });\n (0, _jquery.default)(\"#integration-mlc\").click(mlcSetup);\n (0, _jquery.default)(\"#start-date,#start-time\").change(processDateTime(\"start\"));\n (0, _jquery.default)(\"#end-date,#end-time\").change(processDateTime(\"end\"));\n (0, _jquery.default)(\"#config-color-picker\").on(\"input\", function (_e) {\n (0, _jquery.default)(\"#config-color-input\").val((0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(\"#config-color-reset\").click(function () {\n (0, _jquery.default)(\"#config-color-input\").val(\"\");\n (0, _jquery.default)(\"#config-color-picker\").val(\"\");\n });\n window.addEventListener(\"storage\", function (event) {\n if (event.key == \"integrations\" && event.newValue) {\n var integration = JSON.parse(event.newValue);\n\n if (integration[\"name\"] == \"mlc\") {\n (0, _jquery.default)(\"#integration-mlc\").text(\"Already Configured\").attr(\"disabled\", true);\n window.focus();\n localStorage.removeItem(\"integrations\");\n }\n }\n });\n (0, _jquery.default)(\"#setup-form\").submit(function (e) {\n if ((0, _jquery.default)(\"#newsletter-checkbox\").prop(\"checked\")) {\n var email = (0, _jquery.default)(e.target).find(\"input[name=email]\").val();\n\n _jquery.default.ajax({\n type: \"POST\",\n url: \"https://ctfd.us15.list-manage.com/subscribe/post-json?u=6c7fa6feeced52775aec9d015&id=dd1484208e&c=?\",\n data: {\n EMAIL: email,\n subscribe: \"Subscribe\",\n b_6c7fa6feeced52775aec9d015_dd1484208e: \"\"\n },\n dataType: \"jsonp\",\n contentType: \"application/json; charset=utf-8\"\n });\n }\n });\n});\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/pages/setup.js?");
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/core/assets/js/pages/main.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _momentTimezone = _interopRequireDefault(__webpack_require__(/*! moment-timezone */ \"./node_modules/moment-timezone/index.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! ../CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction switchTab(event) {\n event.preventDefault(); // Handle tab validation\n\n var valid_tab = true;\n (0, _jquery.default)(event.target).closest(\"[role=tabpanel]\").find(\"input,textarea\").each(function (i, e) {\n var $e = (0, _jquery.default)(e);\n var status = e.checkValidity();\n\n if (status === false) {\n $e.removeClass(\"input-filled-valid\");\n $e.addClass(\"input-filled-invalid\");\n valid_tab = false;\n }\n });\n\n if (valid_tab == false) {\n return;\n }\n\n var href = (0, _jquery.default)(event.target).data(\"href\");\n (0, _jquery.default)(\".nav a[href=\\\"\".concat(href, \"\\\"]\")).tab(\"show\");\n}\n\nfunction processDateTime(datetime) {\n return function (_event) {\n var date_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-date\"));\n var time_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-time\"));\n var unix_time = (0, _momentTimezone.default)(\"\".concat(date_picker.val(), \" \").concat(time_picker.val()), \"YYYY-MM-DD HH:mm\").utc().unix();\n\n if (isNaN(unix_time)) {\n (0, _jquery.default)(\"#\".concat(datetime, \"-preview\")).val(\"\");\n } else {\n (0, _jquery.default)(\"#\".concat(datetime, \"-preview\")).val(unix_time);\n }\n };\n}\n\nfunction mlcSetup(_event) {\n var params = {\n name: (0, _jquery.default)(\"#ctf_name\").val(),\n type: \"jeopardy\",\n description: (0, _jquery.default)(\"#ctf_description\").val(),\n user_mode: (0, _jquery.default)(\"#user_mode\").val(),\n event_url: window.location.origin + _CTFd.default.config.urlRoot,\n redirect_url: window.location.origin + _CTFd.default.config.urlRoot + \"/redirect\",\n integration_setup_url: window.location.origin + _CTFd.default.config.urlRoot + \"/setup/integrations\",\n start: (0, _jquery.default)(\"#start-preview\").val(),\n end: (0, _jquery.default)(\"#end-preview\").val(),\n platform: \"CTFd\",\n state: window.STATE\n };\n var ret = [];\n\n for (var p in params) {\n ret.push(encodeURIComponent(p) + \"=\" + encodeURIComponent(params[p]));\n }\n\n window.open(\"https://www.majorleaguecyber.org/events/new?\" + ret.join(\"&\"), \"_blank\");\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".tab-next\").click(switchTab);\n (0, _jquery.default)(\"input\").on(\"keypress\", function (e) {\n // Hook Enter button\n if (e.keyCode == 13) {\n e.preventDefault();\n (0, _jquery.default)(e.target).closest(\".tab-pane\").find(\"button[data-href]\").click();\n }\n });\n (0, _jquery.default)(\"#integration-mlc\").click(mlcSetup);\n (0, _jquery.default)(\"#start-date,#start-time\").change(processDateTime(\"start\"));\n (0, _jquery.default)(\"#end-date,#end-time\").change(processDateTime(\"end\"));\n (0, _jquery.default)(\"#config-color-picker\").on(\"input\", function (_e) {\n (0, _jquery.default)(\"#config-color-input\").val((0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(\"#config-color-reset\").click(function () {\n (0, _jquery.default)(\"#config-color-input\").val(\"\");\n (0, _jquery.default)(\"#config-color-picker\").val(\"\");\n });\n window.addEventListener(\"storage\", function (event) {\n if (event.key == \"integrations\" && event.newValue) {\n var integration = JSON.parse(event.newValue);\n\n if (integration[\"name\"] == \"mlc\") {\n (0, _jquery.default)(\"#integration-mlc\").text(\"Already Configured\").attr(\"disabled\", true);\n window.focus();\n localStorage.removeItem(\"integrations\");\n }\n }\n });\n (0, _jquery.default)(\"#setup-form\").submit(function (e) {\n if ((0, _jquery.default)(\"#newsletter-checkbox\").prop(\"checked\")) {\n var email = (0, _jquery.default)(e.target).find(\"input[name=email]\").val();\n\n _jquery.default.ajax({\n type: \"POST\",\n url: \"https://ctfd.us15.list-manage.com/subscribe/post-json?u=6c7fa6feeced52775aec9d015&id=dd1484208e&c=?\",\n data: {\n EMAIL: email,\n subscribe: \"Subscribe\",\n b_6c7fa6feeced52775aec9d015_dd1484208e: \"\"\n },\n dataType: \"jsonp\",\n contentType: \"application/json; charset=utf-8\"\n });\n }\n });\n});\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/pages/setup.js?");
/***/ })

File diff suppressed because one or more lines are too long

View File

@@ -96,13 +96,13 @@
<div class="row submit-row">
<div class="col-md-9 form-group">
{% block input %}
<input id="challenge-id" type="hidden" value="{{ challenge.id }}">
<input class="form-control" type="text" name="answer" id="submission-input" placeholder="Flag"/>
<input id="challenge-id" class="challenge-id" type="hidden" value="{{ challenge.id }}">
<input id="challenge-input" class="challenge-input" type="text" name="answer" placeholder="Flag"/>
{% endblock %}
</div>
<div class="col-md-3 form-group key-submit">
{% block submit %}
<button type="submit" id="submit-key" class="btn btn-md btn-outline-secondary float-right">
<button id="challenge-submit" class="challenge-submit" type="submit">
Submit
</button>
{% endblock %}
@@ -141,4 +141,4 @@
</div>
</div>
</div>
</div>
</div>

View File

@@ -64,7 +64,7 @@
</small>
</div>
<div class="float-right">
<div class="text-right">
<button type="button" class="btn btn-primary btn-outlined tab-next" data-href="#administration">
Next
</button>
@@ -100,7 +100,7 @@
</label>
</div>
<div class="float-right">
<div class="text-right">
<button type="button" class="btn btn-primary btn-outlined tab-next" data-href="#style">
Next
</button>
@@ -130,7 +130,7 @@
{{ form.theme_color.description }}
</small>
</div>
<div class="float-right">
<div class="text-right">
<button type="button" class="btn btn-primary btn-outlined tab-next" data-href="#datetime">
Next
</button>
@@ -179,7 +179,7 @@
</small>
</div>
<div class="float-right">
<div class="text-right">
<button type="button" class="btn btn-primary btn-outlined tab-next" data-href="#integrations">
Next
</button>
@@ -207,7 +207,7 @@
<hr>
<br>
<div class="submit-row float-right">
<div class="submit-row text-right">
{{ form.submit(class="btn btn-md btn-primary btn-outlined") }}
</div>
</div>

View File

@@ -11,13 +11,13 @@ def build_model_filters(model, query, field, extra_columns=None):
column = getattr(model, field)
if type(column.type) == sqlalchemy.sql.sqltypes.Integer:
_filter = column.op("==")(query)
_filter = column.op("=")(query)
else:
_filter = column.like(f"%{query}%")
filters.append(_filter)
else:
if field in extra_columns:
column = extra_columns[field]
_filter = column.op("==")(query)
_filter = column.op("=")(query)
filters.append(_filter)
return filters

View File

@@ -1,6 +1,6 @@
{
"name": "ctfd",
"version": "3.0.1",
"version": "3.0.2",
"description": "CTFd is a Capture The Flag framework focusing on ease of use and customizability. It comes with everything you need to run a CTF and it's easy to customize with plugins and themes.",
"main": "index.js",
"directories": {

View File

@@ -0,0 +1,41 @@
from CTFd.models import Users
from tests.helpers import (
create_ctfd,
destroy_ctfd,
login_as_user,
register_user,
simulate_user_activity,
)
def test_browse_admin_submissions():
"""Test that an admin can create a challenge properly"""
app = create_ctfd()
with app.app_context():
register_user(app, name="RegisteredUser")
user = Users.query.filter_by(id=2).first()
simulate_user_activity(app.db, user)
admin = login_as_user(app, name="admin", password="password")
# It's difficult to do better checks here becase we're just doing string search.
# incorrect includes the word correct and the navbar has correct and incorrect in it
r = admin.get("/admin/submissions")
assert r.status_code == 200
assert "RegisteredUser" in r.get_data(as_text=True)
assert "correct" in r.get_data(as_text=True)
assert "incorrect" in r.get_data(as_text=True)
r = admin.get("/admin/submissions/correct")
assert r.status_code == 200
assert "RegisteredUser" in r.get_data(as_text=True)
assert "correct" in r.get_data(as_text=True)
r = admin.get("/admin/submissions/incorrect")
assert r.status_code == 200
assert "RegisteredUser" in r.get_data(as_text=True)
r = admin.get("/admin/submissions/correct?field=challenge_id&q=1")
assert r.status_code == 200
assert "RegisteredUser" in r.get_data(as_text=True)
destroy_ctfd(app)