From b8d0f80d0124ce74dec704eb3be8fed6db821357 Mon Sep 17 00:00:00 2001 From: Kevin Chung Date: Sun, 22 Dec 2019 23:17:34 -0500 Subject: [PATCH] 2.2.0 (#1188) 2.2.0 / 2019-12-22 ================== ## Notice 2.2.0 focuses on updating the front end of CTFd to use more modern programming practices and changes some aspects of core CTFd design. If your current installation is using a custom theme or custom plugin with ***any*** kind of JavaScript, it is likely that you will need to upgrade that theme/plugin to be useable with v2.2.0. **General** * Team size limits can now be enforced from the configuration panel * Access tokens functionality for API usage * Admins can now choose how to deliver their notifications * Toast (new default) * Alert * Background * Sound On / Sound Off * There is now a notification counter showing how many unread notifications were received * Setup has been redesigned to have multiple steps * Added Description * Added Start time and End time, * Added MajorLeagueCyber integration * Added Theme and color selection * Fixes issue where updating dynamic challenges could change the value to an incorrect value * Properly use a less restrictive regex to validate email addresses * Bump Python dependencies to latest working versions * Admins can now give awards to team members from the team's admin panel page **API** * Team member removals (`DELETE /api/v1/teams/[team_id]/members`) from the admin panel will now delete the removed members's Submissions, Awards, Unlocks **Admin Panel** * Admins can now user a color input box to specify a theme color which is injected as part of the CSS configuration. Theme developers can use this CSS value to change colors and styles accordingly. * Challenge updates will now alert you if the challenge doesn't have a flag * Challenge entry now allows you to upload files and enter simple flags from the initial challenge creation page **Themes** * Significant JavaScript and CSS rewrite to use ES6, Webpack, yarn, and babel * Theme asset specially generated URLs * Static theme assets are now loaded with either .dev.extension or .min.extension depending on production or development (i.e. debug server) * Static theme assets are also given a `d` GET parameter that changes per server start. Used to bust browser caches. * Use `defer` for script tags to not block page rendering * Only show the MajorLeagueCyber button if configured in configuration * The admin panel now links to https://help.ctfd.io/ in the top right * Create an `ezToast()` function to use [Bootstrap's toasts](https://getbootstrap.com/docs/4.3/components/toasts/) * The user-facing navbar now features icons * Awards shown on a user's profile can now have award icons * The default MarkdownIt render created by CTFd will now open links in new tabs * Country flags can now be shown on the user pages **Deployment** * Switch `Dockerfile` from `python:2.7-alpine` to `python:3.7-alpine` * Add `SERVER_SENT_EVENTS` config value to control whether Notifications are enabled * Challenge ID is now recorded in the submission log **Plugins** * Add an endpoint parameter to `register_plugin_assets_directory()` and `register_plugin_asset()` to control what endpoint Flask uses for the added route **Miscellaneous** * `CTFd.utils.email.sendmail()` now allows the caller to specify subject as an argument * The subject allows for injecting custom variable via the new `CTFd.utils.formatters.safe_format()` function * Admin user information is now error checked during setup * Added yarn to the toolchain and the yarn dev, yarn build, yarn verify, and yarn clean scripts * Prevent old CTFd imports from being imported --- .gitignore | 2 +- .travis.yml | 1 + CHANGELOG.md | 63 + CTFd/__init__.py | 10 +- CTFd/admin/statistics.py | 6 +- CTFd/api/__init__.py | 2 + CTFd/api/v1/challenges.py | 23 +- CTFd/api/v1/notifications.py | 7 + CTFd/api/v1/teams.py | 12 +- CTFd/api/v1/tokens.py | 83 + CTFd/auth.py | 9 + CTFd/config.py | 4 + CTFd/events/__init__.py | 5 + CTFd/exceptions/__init__.py | 6 + CTFd/models/__init__.py | 41 +- CTFd/plugins/__init__.py | 12 +- CTFd/plugins/challenges/assets/create.html | 10 +- CTFd/plugins/challenges/assets/create.js | 68 +- CTFd/plugins/challenges/assets/update.html | 2 +- CTFd/plugins/challenges/assets/view.html | 3 +- CTFd/plugins/challenges/assets/view.js | 65 +- CTFd/plugins/dynamic_challenges/__init__.py | 99 +- .../dynamic_challenges/assets/update.html | 37 +- .../dynamic_challenges/assets/update.js | 51 - .../plugins/dynamic_challenges/assets/view.js | 65 +- CTFd/plugins/flags/assets/regex/create.html | 2 +- CTFd/plugins/flags/assets/regex/edit.html | 2 +- CTFd/plugins/flags/assets/static/create.html | 2 +- CTFd/plugins/flags/assets/static/edit.html | 2 +- CTFd/schemas/tokens.py | 23 + CTFd/teams.py | 44 +- CTFd/themes/admin/assets/css/admin.scss | 68 + .../css/challenge-board.scss} | 8 + .../assets/css/includes/sticky-footer.css | 30 + .../admin/assets/js/challenges/challenge.js | 233 + .../admin/assets/js/challenges/files.js | 42 + .../admin/assets/js/challenges/flags.js | 137 + .../admin/assets/js/challenges/hints.js | 145 + CTFd/themes/admin/assets/js/challenges/new.js | 75 + .../assets/js/challenges/requirements.js | 69 + .../themes/admin/assets/js/challenges/tags.js | 42 + .../themes/admin/assets/js/pages/challenge.js | 490 ++ .../team.js => assets/js/pages/challenges.js} | 0 CTFd/themes/admin/assets/js/pages/configs.js | 298 + .../{static => assets}/js/pages/editor.js | 120 +- CTFd/themes/admin/assets/js/pages/events.js | 7 + CTFd/themes/admin/assets/js/pages/main.js | 16 + .../admin/assets/js/pages/notifications.js | 51 + CTFd/themes/admin/assets/js/pages/pages.js | 37 + CTFd/themes/admin/assets/js/pages/reset.js | 20 + .../admin/assets/js/pages/scoreboard.js | 42 + .../admin/assets/js/pages/statistics.js | 222 + CTFd/themes/admin/assets/js/pages/style.js | 8 + .../admin/assets/js/pages/submissions.js | 45 + CTFd/themes/admin/assets/js/pages/team.js | 422 ++ .../user.js => assets/js/pages/teams.js} | 0 CTFd/themes/admin/assets/js/pages/user.js | 440 ++ CTFd/themes/admin/assets/js/pages/users.js | 1 + CTFd/themes/admin/assets/js/styles.js | 51 + .../js/templates/admin-flags-table.njk | 0 CTFd/themes/admin/static/css/admin.dev.css | 4 + CTFd/themes/admin/static/css/admin.min.css | 1 + CTFd/themes/admin/static/css/base.css | 200 - .../admin/static/css/challenge-board.dev.css | 2 + .../admin/static/css/challenge-board.min.css | 1 + .../themes/admin/static/css/sticky-footer.css | 19 - CTFd/themes/admin/static/css/style.css | 7 - .../admin/static/js/challenges/challenge.js | 237 - .../admin/static/js/challenges/challenges.js | 1 - .../admin/static/js/challenges/files.js | 75 - .../admin/static/js/challenges/flags.js | 134 - .../admin/static/js/challenges/hints.js | 157 - CTFd/themes/admin/static/js/challenges/new.js | 69 - .../static/js/challenges/requirements.js | 63 - .../themes/admin/static/js/challenges/tags.js | 55 - .../themes/admin/static/js/configs/configs.js | 317 - CTFd/themes/admin/static/js/core.dev.js | 146 + CTFd/themes/admin/static/js/core.min.js | 0 CTFd/themes/admin/static/js/files/files.js | 46 - CTFd/themes/admin/static/js/graphs.dev.js | 15 + CTFd/themes/admin/static/js/graphs.min.js | 0 CTFd/themes/admin/static/js/helpers.dev.js | 63 + CTFd/themes/admin/static/js/helpers.min.js | 1 + CTFd/themes/admin/static/js/main.js | 3 - .../static/js/notifications/notifications.js | 51 - .../admin/static/js/pages/challenge.dev.js | 229 + .../admin/static/js/pages/challenge.min.js | 1 + .../admin/static/js/pages/configs.dev.js | 169 + .../admin/static/js/pages/configs.min.js | 1 + .../admin/static/js/pages/editor.dev.js | 169 + .../admin/static/js/pages/editor.min.js | 1 + CTFd/themes/admin/static/js/pages/main.dev.js | 155 + CTFd/themes/admin/static/js/pages/main.min.js | 1 + CTFd/themes/admin/static/js/pages/media.js | 64 - .../static/js/pages/notifications.dev.js | 169 + .../static/js/pages/notifications.min.js | 1 + .../themes/admin/static/js/pages/pages.dev.js | 169 + CTFd/themes/admin/static/js/pages/pages.js | 29 - .../themes/admin/static/js/pages/pages.min.js | 1 + .../themes/admin/static/js/pages/reset.dev.js | 169 + .../themes/admin/static/js/pages/reset.min.js | 1 + .../admin/static/js/pages/scoreboard.dev.js | 169 + .../admin/static/js/pages/scoreboard.min.js | 1 + .../admin/static/js/pages/statistics.dev.js | 169 + .../admin/static/js/pages/statistics.min.js | 1 + .../admin/static/js/pages/submissions.dev.js | 169 + .../admin/static/js/pages/submissions.min.js | 1 + CTFd/themes/admin/static/js/pages/team.dev.js | 169 + CTFd/themes/admin/static/js/pages/team.min.js | 1 + .../themes/admin/static/js/pages/teams.dev.js | 101 + .../themes/admin/static/js/pages/teams.min.js | 1 + CTFd/themes/admin/static/js/pages/user.dev.js | 169 + CTFd/themes/admin/static/js/pages/user.min.js | 1 + .../themes/admin/static/js/pages/users.dev.js | 169 + .../themes/admin/static/js/pages/users.min.js | 1 + .../admin/static/js/plotly.bundle.dev.js | 15 + .../admin/static/js/plotly.bundle.min.js | 13 + .../admin/static/js/scoreboard/scoreboard.js | 41 - .../admin/static/js/statistics/graphs.js | 222 - CTFd/themes/admin/static/js/style.js | 46 - .../static/js/submissions/submissions.js | 44 - CTFd/themes/admin/static/js/teams/actions.js | 145 - CTFd/themes/admin/static/js/teams/graphs.js | 177 - CTFd/themes/admin/static/js/teams/info.js | 77 - CTFd/themes/admin/static/js/teams/new.js | 39 - CTFd/themes/admin/static/js/teams/teams.js | 1 - CTFd/themes/admin/static/js/users/actions.js | 243 - CTFd/themes/admin/static/js/users/graphs.js | 178 - CTFd/themes/admin/static/js/users/info.js | 38 - CTFd/themes/admin/static/js/users/new.js | 44 - CTFd/themes/admin/static/js/users/users.js | 1 - .../admin/static/js/vendor.bundle.dev.js | 2532 +++++++ .../admin/static/js/vendor.bundle.min.js | 472 ++ CTFd/themes/admin/templates/base.html | 69 +- .../admin/templates/challenges/challenge.html | 15 +- .../templates/challenges/challenges.html | 1 - .../admin/templates/challenges/new.html | 75 +- CTFd/themes/admin/templates/config.html | 6 +- .../admin/templates/configs/accounts.html | 16 +- .../admin/templates/configs/appearance.html | 31 +- .../admin/templates/configs/backup.html | 2 +- .../admin/templates/configs/settings.html | 8 +- CTFd/themes/admin/templates/configs/time.html | 27 +- CTFd/themes/admin/templates/editor.html | 26 +- CTFd/themes/admin/templates/integrations.html | 35 + .../admin/templates/modals/awards/create.html | 111 + .../modals/challenges/requirements.html | 2 +- .../templates/modals/challenges/solves.html | 4 +- .../templates/modals/challenges/tags.html | 2 +- .../admin/templates/modals/flags/create.html | 2 +- .../admin/templates/modals/teams/captain.html | 2 +- .../admin/templates/modals/teams/create.html | 6 +- .../admin/templates/modals/teams/edit.html | 57 + .../admin/templates/modals/users/create.html | 6 +- .../admin/templates/modals/users/edit.html | 12 +- .../themes/admin/templates/notifications.html | 47 +- CTFd/themes/admin/templates/pages.html | 6 +- CTFd/themes/admin/templates/reset.html | 16 +- CTFd/themes/admin/templates/scoreboard.html | 13 +- CTFd/themes/admin/templates/statistics.html | 15 +- CTFd/themes/admin/templates/submissions.html | 11 +- CTFd/themes/admin/templates/teams/new.html | 9 +- CTFd/themes/admin/templates/teams/team.html | 68 +- CTFd/themes/admin/templates/teams/teams.html | 12 +- CTFd/themes/admin/templates/users/new.html | 7 +- CTFd/themes/admin/templates/users/user.html | 45 +- CTFd/themes/admin/templates/users/users.html | 16 +- .../css/challenge-board.scss} | 0 CTFd/themes/core/assets/css/codemirror.scss | 1 + CTFd/themes/core/assets/css/core.scss | 45 + CTFd/themes/core/assets/css/fonts.scss | 13 + .../core/assets/css/includes/award-icons.scss | 39 + .../core/assets/css/includes/flag-icons.scss | 511 ++ .../assets/css/includes}/jumbotron.css | 0 .../assets/css/includes/sticky-footer.css | 31 + .../css/base.css => assets/css/main.scss} | 44 +- CTFd/themes/core/assets/js/CTFd.js | 56 + CTFd/themes/core/assets/js/api.js | 3613 +++++++++ CTFd/themes/core/assets/js/config.js | 5 + CTFd/themes/core/assets/js/events.js | 110 + CTFd/themes/core/assets/js/ezq.js | 206 + CTFd/themes/core/assets/js/fetch.js | 25 + CTFd/themes/core/assets/js/graphs.js | 190 + CTFd/themes/core/assets/js/helpers.js | 64 + .../themes/core/assets/js/pages/challenges.js | 430 ++ CTFd/themes/core/assets/js/pages/events.js | 7 + CTFd/themes/core/assets/js/pages/main.js | 17 + .../core/assets/js/pages/notifications.js | 8 + .../themes/core/assets/js/pages/scoreboard.js | 162 + CTFd/themes/core/assets/js/pages/settings.js | 127 + CTFd/themes/core/assets/js/pages/setup.js | 112 + CTFd/themes/core/assets/js/pages/stats.js | 103 + CTFd/themes/core/assets/js/pages/style.js | 8 + .../core/assets/js/pages/teams/private.js | 96 + CTFd/themes/core/assets/js/patch.js | 387 + CTFd/themes/core/assets/js/styles.js | 25 + CTFd/themes/core/assets/js/times.js | 10 + CTFd/themes/core/assets/js/utils.js | 253 + .../core/static/css/challenge-board.dev.css | 2 + .../core/static/css/challenge-board.min.css | 1 + .../themes/core/static/css/codemirror.dev.css | 4 + .../themes/core/static/css/codemirror.min.css | 1 + CTFd/themes/core/static/css/core.dev.css | 2 + CTFd/themes/core/static/css/core.min.css | 1 + CTFd/themes/core/static/css/fonts.dev.css | 20 + CTFd/themes/core/static/css/fonts.min.css | 15 + CTFd/themes/core/static/css/jumbotron.css | 4 - CTFd/themes/core/static/css/main.dev.css | 13 + CTFd/themes/core/static/css/main.min.css | 6 + CTFd/themes/core/static/css/sticky-footer.css | 19 - CTFd/themes/core/static/css/style.css | 403 - .../core/static/css/vendor/bootstrap.min.css | 7 - .../core/static/css/vendor/codemirror.min.css | 1 - .../vendor/font-awesome/fontawesome-all.css | 3935 ---------- .../font-awesome/fontawesome-all.min.css | 5 - .../vendor/font-awesome/fontawesome-fonts.css | 22 - .../font-awesome/webfonts/fa-brands-400.eot | Bin 123540 -> 0 bytes .../font-awesome/webfonts/fa-brands-400.svg | 1175 --- .../font-awesome/webfonts/fa-brands-400.ttf | Bin 123304 -> 0 bytes .../font-awesome/webfonts/fa-brands-400.woff | Bin 79752 -> 0 bytes .../font-awesome/webfonts/fa-brands-400.woff2 | Bin 68240 -> 0 bytes .../font-awesome/webfonts/fa-regular-400.eot | Bin 40576 -> 0 bytes .../font-awesome/webfonts/fa-regular-400.svg | 467 -- .../font-awesome/webfonts/fa-regular-400.ttf | Bin 40348 -> 0 bytes .../font-awesome/webfonts/fa-regular-400.woff | Bin 18168 -> 0 bytes .../webfonts/fa-regular-400.woff2 | Bin 14880 -> 0 bytes .../font-awesome/webfonts/fa-solid-900.eot | Bin 191332 -> 0 bytes .../font-awesome/webfonts/fa-solid-900.svg | 2564 ------- .../font-awesome/webfonts/fa-solid-900.ttf | Bin 191112 -> 0 bytes .../font-awesome/webfonts/fa-solid-900.woff | Bin 92696 -> 0 bytes .../font-awesome/webfonts/fa-solid-900.woff2 | Bin 72000 -> 0 bytes CTFd/themes/core/static/css/vendor/font.css | 146 - .../core/static/css/vendor/lato/Lato_400.eot | Bin 26332 -> 0 bytes .../core/static/css/vendor/lato/Lato_400.svg | 435 -- .../core/static/css/vendor/lato/Lato_400.ttf | Bin 60100 -> 0 bytes .../core/static/css/vendor/lato/Lato_400i.eot | Bin 27523 -> 0 bytes .../core/static/css/vendor/lato/Lato_400i.svg | 450 -- .../core/static/css/vendor/lato/Lato_400i.ttf | Bin 60556 -> 0 bytes .../core/static/css/vendor/lato/Lato_700.eot | Bin 25911 -> 0 bytes .../core/static/css/vendor/lato/Lato_700.svg | 438 -- .../core/static/css/vendor/lato/Lato_700.ttf | Bin 58692 -> 0 bytes .../core/static/css/vendor/lato/Lato_700i.eot | Bin 27659 -> 0 bytes .../core/static/css/vendor/lato/Lato_700i.svg | 451 -- .../core/static/css/vendor/lato/Lato_700i.ttf | Bin 61912 -> 0 bytes .../static/css/vendor/raleway/Raleway_400.eot | Bin 23428 -> 0 bytes .../static/css/vendor/raleway/Raleway_400.svg | 347 - .../static/css/vendor/raleway/Raleway_400.ttf | Bin 53356 -> 0 bytes .../css/vendor/raleway/Raleway_400i.eot | Bin 24611 -> 0 bytes .../css/vendor/raleway/Raleway_400i.svg | 361 - .../css/vendor/raleway/Raleway_400i.ttf | Bin 55764 -> 0 bytes .../static/css/vendor/raleway/Raleway_700.eot | Bin 23794 -> 0 bytes .../static/css/vendor/raleway/Raleway_700.svg | 343 - .../static/css/vendor/raleway/Raleway_700.ttf | Bin 53732 -> 0 bytes .../css/vendor/raleway/Raleway_700i.eot | Bin 24928 -> 0 bytes .../css/vendor/raleway/Raleway_700i.svg | 352 - .../css/vendor/raleway/Raleway_700i.ttf | Bin 56060 -> 0 bytes .../core/static/fonts/fa-brands-400.eot | Bin 0 -> 130906 bytes .../core/static/fonts/fa-brands-400.svg | 3496 +++++++++ .../core/static/fonts/fa-brands-400.ttf | Bin 0 -> 130600 bytes .../core/static/fonts/fa-brands-400.woff | Bin 0 -> 88428 bytes .../core/static/fonts/fa-brands-400.woff2 | Bin 0 -> 75336 bytes .../core/static/fonts/fa-regular-400.eot | Bin 0 -> 34394 bytes .../core/static/fonts/fa-regular-400.svg | 803 ++ .../core/static/fonts/fa-regular-400.ttf | Bin 0 -> 34096 bytes .../core/static/fonts/fa-regular-400.woff | Bin 0 -> 16804 bytes .../core/static/fonts/fa-regular-400.woff2 | Bin 0 -> 13584 bytes .../themes/core/static/fonts/fa-solid-900.eot | Bin 0 -> 192758 bytes .../themes/core/static/fonts/fa-solid-900.svg | 4667 ++++++++++++ .../themes/core/static/fonts/fa-solid-900.ttf | Bin 0 -> 192472 bytes .../core/static/fonts/fa-solid-900.woff | Bin 0 -> 98384 bytes .../core/static/fonts/fa-solid-900.woff2 | Bin 0 -> 75728 bytes .../core/static/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../core/static/fonts/fontawesome-webfont.svg | 2671 +++++++ .../core/static/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../static/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../static/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes .../core/static/fonts/lato-latin-100.woff | Bin 0 -> 26860 bytes .../core/static/fonts/lato-latin-100.woff2 | Bin 0 -> 21372 bytes .../static/fonts/lato-latin-100italic.woff | Bin 0 -> 21804 bytes .../static/fonts/lato-latin-100italic.woff2 | Bin 0 -> 17004 bytes .../core/static/fonts/lato-latin-300.woff | Bin 0 -> 29852 bytes .../core/static/fonts/lato-latin-300.woff2 | Bin 0 -> 23208 bytes .../static/fonts/lato-latin-300italic.woff | Bin 0 -> 22312 bytes .../static/fonts/lato-latin-300italic.woff2 | Bin 0 -> 17528 bytes .../lato-latin-400.woff} | Bin .../lato-latin-400.woff2} | Bin .../lato-latin-400italic.woff} | Bin .../lato-latin-400italic.woff2} | Bin .../lato-latin-700.woff} | Bin .../lato-latin-700.woff2} | Bin .../lato-latin-700italic.woff} | Bin .../lato-latin-700italic.woff2} | Bin .../core/static/fonts/lato-latin-900.woff | Bin 0 -> 27260 bytes .../core/static/fonts/lato-latin-900.woff2 | Bin 0 -> 22352 bytes .../static/fonts/lato-latin-900italic.woff | Bin 0 -> 28688 bytes .../static/fonts/lato-latin-900italic.woff2 | Bin 0 -> 23524 bytes .../core/static/fonts/raleway-latin-100.woff | Bin 0 -> 23948 bytes .../core/static/fonts/raleway-latin-100.woff2 | Bin 0 -> 19660 bytes .../static/fonts/raleway-latin-100italic.woff | Bin 0 -> 25028 bytes .../fonts/raleway-latin-100italic.woff2 | Bin 0 -> 20712 bytes .../core/static/fonts/raleway-latin-200.woff | Bin 0 -> 24644 bytes .../core/static/fonts/raleway-latin-200.woff2 | Bin 0 -> 20200 bytes .../static/fonts/raleway-latin-200italic.woff | Bin 0 -> 25572 bytes .../fonts/raleway-latin-200italic.woff2 | Bin 0 -> 21084 bytes .../core/static/fonts/raleway-latin-300.woff | Bin 0 -> 25008 bytes .../core/static/fonts/raleway-latin-300.woff2 | Bin 0 -> 20512 bytes .../static/fonts/raleway-latin-300italic.woff | Bin 0 -> 26164 bytes .../fonts/raleway-latin-300italic.woff2 | Bin 0 -> 21488 bytes .../raleway-latin-400.woff} | Bin .../raleway-latin-400.woff2} | Bin .../raleway-latin-400italic.woff} | Bin .../raleway-latin-400italic.woff2} | Bin .../core/static/fonts/raleway-latin-500.woff | Bin 0 -> 25200 bytes .../core/static/fonts/raleway-latin-500.woff2 | Bin 0 -> 20820 bytes .../static/fonts/raleway-latin-500italic.woff | Bin 0 -> 25804 bytes .../fonts/raleway-latin-500italic.woff2 | Bin 0 -> 21328 bytes .../core/static/fonts/raleway-latin-600.woff | Bin 0 -> 25064 bytes .../core/static/fonts/raleway-latin-600.woff2 | Bin 0 -> 20644 bytes .../static/fonts/raleway-latin-600italic.woff | Bin 0 -> 26100 bytes .../fonts/raleway-latin-600italic.woff2 | Bin 0 -> 21492 bytes .../raleway-latin-700.woff} | Bin .../raleway-latin-700.woff2} | Bin .../raleway-latin-700italic.woff} | Bin .../raleway-latin-700italic.woff2} | Bin .../core/static/fonts/raleway-latin-800.woff | Bin 0 -> 24980 bytes .../core/static/fonts/raleway-latin-800.woff2 | Bin 0 -> 20484 bytes .../static/fonts/raleway-latin-800italic.woff | Bin 0 -> 26172 bytes .../fonts/raleway-latin-800italic.woff2 | Bin 0 -> 21536 bytes .../core/static/fonts/raleway-latin-900.woff | Bin 0 -> 25340 bytes .../core/static/fonts/raleway-latin-900.woff2 | Bin 0 -> 20840 bytes .../static/fonts/raleway-latin-900italic.woff | Bin 0 -> 26344 bytes .../fonts/raleway-latin-900italic.woff2 | Bin 0 -> 21688 bytes CTFd/themes/core/static/js/CTFd.js | 46 - CTFd/themes/core/static/js/challenges.js | 408 - CTFd/themes/core/static/js/core.dev.js | 146 + CTFd/themes/core/static/js/core.min.js | 0 CTFd/themes/core/static/js/events.js | 49 - CTFd/themes/core/static/js/ezq.js | 116 - CTFd/themes/core/static/js/helpers.dev.js | 63 + CTFd/themes/core/static/js/helpers.min.js | 1 + CTFd/themes/core/static/js/hints.js | 71 - CTFd/themes/core/static/js/multi-modal.js | 68 - .../core/static/js/pages/challenges.dev.js | 169 + .../core/static/js/pages/challenges.min.js | 1 + CTFd/themes/core/static/js/pages/main.dev.js | 155 + CTFd/themes/core/static/js/pages/main.min.js | 1 + .../core/static/js/pages/notifications.dev.js | 169 + .../core/static/js/pages/notifications.min.js | 1 + .../core/static/js/pages/scoreboard.dev.js | 169 + .../core/static/js/pages/scoreboard.min.js | 1 + .../core/static/js/pages/settings.dev.js | 169 + .../core/static/js/pages/settings.min.js | 1 + CTFd/themes/core/static/js/pages/setup.dev.js | 169 + CTFd/themes/core/static/js/pages/setup.min.js | 1 + CTFd/themes/core/static/js/pages/stats.dev.js | 181 + CTFd/themes/core/static/js/pages/stats.min.js | 1 + .../core/static/js/pages/teams/private.dev.js | 169 + .../core/static/js/pages/teams/private.min.js | 1 + .../core/static/js/plotly.bundle.dev.js | 15 + .../core/static/js/plotly.bundle.min.js | 13 + CTFd/themes/core/static/js/scoreboard.js | 121 - CTFd/themes/core/static/js/settings.js | 55 - CTFd/themes/core/static/js/style.js | 22 - CTFd/themes/core/static/js/team.js | 180 - CTFd/themes/core/static/js/user.js | 180 - CTFd/themes/core/static/js/utils.js | 123 - .../core/static/js/vendor.bundle.dev.js | 2520 +++++++ .../core/static/js/vendor.bundle.min.js | 472 ++ .../static/js/vendor/bootstrap.bundle.min.js | 7 - .../core/static/js/vendor/codemirror.min.js | 1 - .../core/static/js/vendor/eventsource.min.js | 6 - .../themes/core/static/js/vendor/fetch.min.js | 1 - .../core/static/js/vendor/howler.min.js | 4 - .../core/static/js/vendor/jquery.min.js | 2 - .../core/static/js/vendor/markdown-it.min.js | 1 - .../vendor/moment-timezone-with-data.min.js | 1 - .../core/static/js/vendor/moment.min.js | 1 - .../core/static/js/vendor/nunjucks.min.js | 4 - .../core/static/js/vendor/plotly.min.js | 80 - .../static/js/vendor/promise-polyfill.min.js | 1 - .../static/js/vendor/window-controller.js | 105 - CTFd/themes/core/templates/base.html | 142 +- CTFd/themes/core/templates/challenges.html | 16 +- CTFd/themes/core/templates/confirm.html | 1 - CTFd/themes/core/templates/login.html | 7 +- CTFd/themes/core/templates/notifications.html | 10 +- CTFd/themes/core/templates/register.html | 6 +- .../themes/core/templates/reset_password.html | 1 - CTFd/themes/core/templates/scoreboard.html | 8 +- CTFd/themes/core/templates/settings.html | 201 +- CTFd/themes/core/templates/setup.html | 282 +- .../core/templates/teams/join_team.html | 8 + .../themes/core/templates/teams/new_team.html | 8 + CTFd/themes/core/templates/teams/private.html | 138 +- CTFd/themes/core/templates/teams/public.html | 45 +- .../core/templates/teams/team_enrollment.html | 16 +- CTFd/themes/core/templates/teams/teams.html | 9 +- CTFd/themes/core/templates/users/private.html | 41 +- CTFd/themes/core/templates/users/public.html | 43 +- CTFd/themes/core/templates/users/users.html | 7 +- CTFd/utils/config/__init__.py | 2 +- CTFd/utils/config/integrations.py | 5 + CTFd/utils/config/pages.py | 4 +- CTFd/utils/email/__init__.py | 16 +- CTFd/utils/email/mailgun.py | 4 +- CTFd/utils/email/smtp.py | 5 +- CTFd/utils/encoding/__init__.py | 16 +- CTFd/utils/exports/__init__.py | 60 +- CTFd/utils/formatters/__init__.py | 11 + CTFd/utils/helpers/__init__.py | 24 +- CTFd/utils/initialization/__init__.py | 40 +- CTFd/utils/migrations/__init__.py | 2 +- CTFd/utils/security/auth.py | 30 + CTFd/utils/security/csrf.py | 2 +- CTFd/utils/security/signing.py | 10 +- CTFd/utils/uploads/uploaders.py | 6 +- CTFd/utils/validators/__init__.py | 4 +- CTFd/views.py | 136 +- Dockerfile | 2 +- Makefile | 9 +- docs/conf.py | 2 +- docs/configuration.rst | 4 + .../versions/080d29b15cd3_add_tokens_table.py | 34 + package.json | 62 +- populate.py | 19 +- requirements.txt | 35 +- serve.py | 3 +- tests/api/test_tokens.py | 78 + tests/api/v1/teams/test_scoring.py | 48 + tests/api/v1/teams/test_team_members.py | 37 +- tests/api/v1/test_challenges.py | 4 +- tests/api/v1/test_scoreboard.py | 6 +- tests/api/v1/test_tokens.py | 127 + tests/api/v1/test_users.py | 13 +- tests/api/v1/user/test_challenges.py | 2 +- tests/api/v1/users/__init__.py | 0 tests/api/v1/users/test_scoring.py | 44 + tests/challenges/test_dynamic.py | 32 + tests/helpers.py | 23 +- tests/oauth/test_teams.py | 32 + tests/teams/test_teams.py | 37 +- tests/test_config.py | 19 +- tests/test_setup.py | 36 + tests/test_themes.py | 3 +- tests/test_views.py | 9 +- tests/users/test_setup.py | 41 +- tests/utils/test_email.py | 22 +- tests/utils/test_encoding.py | 4 +- tests/utils/test_formatters.py | 13 + tests/utils/test_updates.py | 8 +- tests/utils/test_validators.py | 22 +- webpack.config.js | 258 + yarn.lock | 6594 ++++++++++++++++- 453 files changed, 41266 insertions(+), 17454 deletions(-) create mode 100644 CTFd/api/v1/tokens.py create mode 100644 CTFd/exceptions/__init__.py create mode 100644 CTFd/schemas/tokens.py create mode 100644 CTFd/themes/admin/assets/css/admin.scss rename CTFd/themes/admin/{static/css/challenge-board.css => assets/css/challenge-board.scss} (80%) create mode 100644 CTFd/themes/admin/assets/css/includes/sticky-footer.css create mode 100644 CTFd/themes/admin/assets/js/challenges/challenge.js create mode 100644 CTFd/themes/admin/assets/js/challenges/files.js create mode 100644 CTFd/themes/admin/assets/js/challenges/flags.js create mode 100644 CTFd/themes/admin/assets/js/challenges/hints.js create mode 100644 CTFd/themes/admin/assets/js/challenges/new.js create mode 100644 CTFd/themes/admin/assets/js/challenges/requirements.js create mode 100644 CTFd/themes/admin/assets/js/challenges/tags.js create mode 100644 CTFd/themes/admin/assets/js/pages/challenge.js rename CTFd/themes/admin/{static/js/teams/team.js => assets/js/pages/challenges.js} (100%) create mode 100644 CTFd/themes/admin/assets/js/pages/configs.js rename CTFd/themes/admin/{static => assets}/js/pages/editor.js (66%) create mode 100644 CTFd/themes/admin/assets/js/pages/events.js create mode 100644 CTFd/themes/admin/assets/js/pages/main.js create mode 100644 CTFd/themes/admin/assets/js/pages/notifications.js create mode 100644 CTFd/themes/admin/assets/js/pages/pages.js create mode 100644 CTFd/themes/admin/assets/js/pages/reset.js create mode 100644 CTFd/themes/admin/assets/js/pages/scoreboard.js create mode 100644 CTFd/themes/admin/assets/js/pages/statistics.js create mode 100644 CTFd/themes/admin/assets/js/pages/style.js create mode 100644 CTFd/themes/admin/assets/js/pages/submissions.js create mode 100644 CTFd/themes/admin/assets/js/pages/team.js rename CTFd/themes/admin/{static/js/users/user.js => assets/js/pages/teams.js} (100%) create mode 100644 CTFd/themes/admin/assets/js/pages/user.js create mode 100644 CTFd/themes/admin/assets/js/pages/users.js create mode 100644 CTFd/themes/admin/assets/js/styles.js rename CTFd/themes/admin/{static => assets}/js/templates/admin-flags-table.njk (100%) create mode 100644 CTFd/themes/admin/static/css/admin.dev.css create mode 100644 CTFd/themes/admin/static/css/admin.min.css delete mode 100644 CTFd/themes/admin/static/css/base.css create mode 100644 CTFd/themes/admin/static/css/challenge-board.dev.css create mode 100644 CTFd/themes/admin/static/css/challenge-board.min.css delete mode 100644 CTFd/themes/admin/static/css/sticky-footer.css delete mode 100644 CTFd/themes/admin/static/css/style.css delete mode 100644 CTFd/themes/admin/static/js/challenges/challenge.js delete mode 100644 CTFd/themes/admin/static/js/challenges/challenges.js delete mode 100644 CTFd/themes/admin/static/js/challenges/files.js delete mode 100644 CTFd/themes/admin/static/js/challenges/flags.js delete mode 100644 CTFd/themes/admin/static/js/challenges/hints.js delete mode 100644 CTFd/themes/admin/static/js/challenges/new.js delete mode 100644 CTFd/themes/admin/static/js/challenges/requirements.js delete mode 100644 CTFd/themes/admin/static/js/challenges/tags.js delete mode 100644 CTFd/themes/admin/static/js/configs/configs.js create mode 100644 CTFd/themes/admin/static/js/core.dev.js create mode 100644 CTFd/themes/admin/static/js/core.min.js delete mode 100644 CTFd/themes/admin/static/js/files/files.js create mode 100644 CTFd/themes/admin/static/js/graphs.dev.js create mode 100644 CTFd/themes/admin/static/js/graphs.min.js create mode 100644 CTFd/themes/admin/static/js/helpers.dev.js create mode 100644 CTFd/themes/admin/static/js/helpers.min.js delete mode 100644 CTFd/themes/admin/static/js/main.js delete mode 100644 CTFd/themes/admin/static/js/notifications/notifications.js create mode 100644 CTFd/themes/admin/static/js/pages/challenge.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/challenge.min.js create mode 100644 CTFd/themes/admin/static/js/pages/configs.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/configs.min.js create mode 100644 CTFd/themes/admin/static/js/pages/editor.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/editor.min.js create mode 100644 CTFd/themes/admin/static/js/pages/main.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/main.min.js delete mode 100644 CTFd/themes/admin/static/js/pages/media.js create mode 100644 CTFd/themes/admin/static/js/pages/notifications.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/notifications.min.js create mode 100644 CTFd/themes/admin/static/js/pages/pages.dev.js delete mode 100644 CTFd/themes/admin/static/js/pages/pages.js create mode 100644 CTFd/themes/admin/static/js/pages/pages.min.js create mode 100644 CTFd/themes/admin/static/js/pages/reset.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/reset.min.js create mode 100644 CTFd/themes/admin/static/js/pages/scoreboard.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/scoreboard.min.js create mode 100644 CTFd/themes/admin/static/js/pages/statistics.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/statistics.min.js create mode 100644 CTFd/themes/admin/static/js/pages/submissions.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/submissions.min.js create mode 100644 CTFd/themes/admin/static/js/pages/team.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/team.min.js create mode 100644 CTFd/themes/admin/static/js/pages/teams.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/teams.min.js create mode 100644 CTFd/themes/admin/static/js/pages/user.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/user.min.js create mode 100644 CTFd/themes/admin/static/js/pages/users.dev.js create mode 100644 CTFd/themes/admin/static/js/pages/users.min.js create mode 100644 CTFd/themes/admin/static/js/plotly.bundle.dev.js create mode 100644 CTFd/themes/admin/static/js/plotly.bundle.min.js delete mode 100644 CTFd/themes/admin/static/js/scoreboard/scoreboard.js delete mode 100644 CTFd/themes/admin/static/js/statistics/graphs.js delete mode 100644 CTFd/themes/admin/static/js/style.js delete mode 100644 CTFd/themes/admin/static/js/submissions/submissions.js delete mode 100644 CTFd/themes/admin/static/js/teams/actions.js delete mode 100644 CTFd/themes/admin/static/js/teams/graphs.js delete mode 100644 CTFd/themes/admin/static/js/teams/info.js delete mode 100644 CTFd/themes/admin/static/js/teams/new.js delete mode 100644 CTFd/themes/admin/static/js/teams/teams.js delete mode 100644 CTFd/themes/admin/static/js/users/actions.js delete mode 100644 CTFd/themes/admin/static/js/users/graphs.js delete mode 100644 CTFd/themes/admin/static/js/users/info.js delete mode 100644 CTFd/themes/admin/static/js/users/new.js delete mode 100644 CTFd/themes/admin/static/js/users/users.js create mode 100644 CTFd/themes/admin/static/js/vendor.bundle.dev.js create mode 100644 CTFd/themes/admin/static/js/vendor.bundle.min.js create mode 100644 CTFd/themes/admin/templates/integrations.html create mode 100644 CTFd/themes/admin/templates/modals/teams/edit.html rename CTFd/themes/core/{static/css/challenge-board.css => assets/css/challenge-board.scss} (100%) create mode 100644 CTFd/themes/core/assets/css/codemirror.scss create mode 100644 CTFd/themes/core/assets/css/core.scss create mode 100644 CTFd/themes/core/assets/css/fonts.scss create mode 100644 CTFd/themes/core/assets/css/includes/award-icons.scss create mode 100644 CTFd/themes/core/assets/css/includes/flag-icons.scss rename CTFd/themes/{admin/static/css => core/assets/css/includes}/jumbotron.css (100%) create mode 100644 CTFd/themes/core/assets/css/includes/sticky-footer.css rename CTFd/themes/core/{static/css/base.css => assets/css/main.scss} (80%) create mode 100644 CTFd/themes/core/assets/js/CTFd.js create mode 100644 CTFd/themes/core/assets/js/api.js create mode 100644 CTFd/themes/core/assets/js/config.js create mode 100644 CTFd/themes/core/assets/js/events.js create mode 100644 CTFd/themes/core/assets/js/ezq.js create mode 100644 CTFd/themes/core/assets/js/fetch.js create mode 100644 CTFd/themes/core/assets/js/graphs.js create mode 100644 CTFd/themes/core/assets/js/helpers.js create mode 100644 CTFd/themes/core/assets/js/pages/challenges.js create mode 100644 CTFd/themes/core/assets/js/pages/events.js create mode 100644 CTFd/themes/core/assets/js/pages/main.js create mode 100644 CTFd/themes/core/assets/js/pages/notifications.js create mode 100644 CTFd/themes/core/assets/js/pages/scoreboard.js create mode 100644 CTFd/themes/core/assets/js/pages/settings.js create mode 100644 CTFd/themes/core/assets/js/pages/setup.js create mode 100644 CTFd/themes/core/assets/js/pages/stats.js create mode 100644 CTFd/themes/core/assets/js/pages/style.js create mode 100644 CTFd/themes/core/assets/js/pages/teams/private.js create mode 100644 CTFd/themes/core/assets/js/patch.js create mode 100644 CTFd/themes/core/assets/js/styles.js create mode 100644 CTFd/themes/core/assets/js/times.js create mode 100644 CTFd/themes/core/assets/js/utils.js create mode 100644 CTFd/themes/core/static/css/challenge-board.dev.css create mode 100644 CTFd/themes/core/static/css/challenge-board.min.css create mode 100644 CTFd/themes/core/static/css/codemirror.dev.css create mode 100644 CTFd/themes/core/static/css/codemirror.min.css create mode 100644 CTFd/themes/core/static/css/core.dev.css create mode 100644 CTFd/themes/core/static/css/core.min.css create mode 100644 CTFd/themes/core/static/css/fonts.dev.css create mode 100644 CTFd/themes/core/static/css/fonts.min.css delete mode 100644 CTFd/themes/core/static/css/jumbotron.css create mode 100644 CTFd/themes/core/static/css/main.dev.css create mode 100644 CTFd/themes/core/static/css/main.min.css delete mode 100644 CTFd/themes/core/static/css/sticky-footer.css delete mode 100644 CTFd/themes/core/static/css/style.css delete mode 100644 CTFd/themes/core/static/css/vendor/bootstrap.min.css delete mode 100644 CTFd/themes/core/static/css/vendor/codemirror.min.css delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/fontawesome-all.css delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/fontawesome-all.min.css delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/fontawesome-fonts.css delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-brands-400.eot delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-brands-400.svg delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-brands-400.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-brands-400.woff delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-brands-400.woff2 delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-regular-400.eot delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-regular-400.svg delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-regular-400.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-regular-400.woff delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-regular-400.woff2 delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-solid-900.eot delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-solid-900.svg delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-solid-900.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-solid-900.woff delete mode 100644 CTFd/themes/core/static/css/vendor/font-awesome/webfonts/fa-solid-900.woff2 delete mode 100644 CTFd/themes/core/static/css/vendor/font.css delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_400.eot delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_400.svg delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_400.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_400i.eot delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_400i.svg delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_400i.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_700.eot delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_700.svg delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_700.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_700i.eot delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_700i.svg delete mode 100644 CTFd/themes/core/static/css/vendor/lato/Lato_700i.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_400.eot delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_400.svg delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_400.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_400i.eot delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_400i.svg delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_400i.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_700.eot delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_700.svg delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_700.ttf delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_700i.eot delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_700i.svg delete mode 100644 CTFd/themes/core/static/css/vendor/raleway/Raleway_700i.ttf create mode 100644 CTFd/themes/core/static/fonts/fa-brands-400.eot create mode 100644 CTFd/themes/core/static/fonts/fa-brands-400.svg create mode 100644 CTFd/themes/core/static/fonts/fa-brands-400.ttf create mode 100644 CTFd/themes/core/static/fonts/fa-brands-400.woff create mode 100644 CTFd/themes/core/static/fonts/fa-brands-400.woff2 create mode 100644 CTFd/themes/core/static/fonts/fa-regular-400.eot create mode 100644 CTFd/themes/core/static/fonts/fa-regular-400.svg create mode 100644 CTFd/themes/core/static/fonts/fa-regular-400.ttf create mode 100644 CTFd/themes/core/static/fonts/fa-regular-400.woff create mode 100644 CTFd/themes/core/static/fonts/fa-regular-400.woff2 create mode 100644 CTFd/themes/core/static/fonts/fa-solid-900.eot create mode 100644 CTFd/themes/core/static/fonts/fa-solid-900.svg create mode 100644 CTFd/themes/core/static/fonts/fa-solid-900.ttf create mode 100644 CTFd/themes/core/static/fonts/fa-solid-900.woff create mode 100644 CTFd/themes/core/static/fonts/fa-solid-900.woff2 create mode 100644 CTFd/themes/core/static/fonts/fontawesome-webfont.eot create mode 100644 CTFd/themes/core/static/fonts/fontawesome-webfont.svg create mode 100644 CTFd/themes/core/static/fonts/fontawesome-webfont.ttf create mode 100644 CTFd/themes/core/static/fonts/fontawesome-webfont.woff create mode 100644 CTFd/themes/core/static/fonts/fontawesome-webfont.woff2 create mode 100644 CTFd/themes/core/static/fonts/lato-latin-100.woff create mode 100644 CTFd/themes/core/static/fonts/lato-latin-100.woff2 create mode 100644 CTFd/themes/core/static/fonts/lato-latin-100italic.woff create mode 100644 CTFd/themes/core/static/fonts/lato-latin-100italic.woff2 create mode 100644 CTFd/themes/core/static/fonts/lato-latin-300.woff create mode 100644 CTFd/themes/core/static/fonts/lato-latin-300.woff2 create mode 100644 CTFd/themes/core/static/fonts/lato-latin-300italic.woff create mode 100644 CTFd/themes/core/static/fonts/lato-latin-300italic.woff2 rename CTFd/themes/core/static/{css/vendor/lato/Lato_400.woff => fonts/lato-latin-400.woff} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_400.woff2 => fonts/lato-latin-400.woff2} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_400i.woff => fonts/lato-latin-400italic.woff} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_400i.woff2 => fonts/lato-latin-400italic.woff2} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_700.woff => fonts/lato-latin-700.woff} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_700.woff2 => fonts/lato-latin-700.woff2} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_700i.woff => fonts/lato-latin-700italic.woff} (100%) rename CTFd/themes/core/static/{css/vendor/lato/Lato_700i.woff2 => fonts/lato-latin-700italic.woff2} (100%) create mode 100644 CTFd/themes/core/static/fonts/lato-latin-900.woff create mode 100644 CTFd/themes/core/static/fonts/lato-latin-900.woff2 create mode 100644 CTFd/themes/core/static/fonts/lato-latin-900italic.woff create mode 100644 CTFd/themes/core/static/fonts/lato-latin-900italic.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-100.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-100.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-100italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-100italic.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-200.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-200.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-200italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-200italic.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-300.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-300.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-300italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-300italic.woff2 rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_400.woff => fonts/raleway-latin-400.woff} (100%) rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_400.woff2 => fonts/raleway-latin-400.woff2} (100%) rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_400i.woff => fonts/raleway-latin-400italic.woff} (100%) rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_400i.woff2 => fonts/raleway-latin-400italic.woff2} (100%) create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-500.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-500.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-500italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-500italic.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-600.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-600.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-600italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-600italic.woff2 rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_700.woff => fonts/raleway-latin-700.woff} (100%) rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_700.woff2 => fonts/raleway-latin-700.woff2} (100%) rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_700i.woff => fonts/raleway-latin-700italic.woff} (100%) rename CTFd/themes/core/static/{css/vendor/raleway/Raleway_700i.woff2 => fonts/raleway-latin-700italic.woff2} (100%) create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-800.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-800.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-800italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-800italic.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-900.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-900.woff2 create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-900italic.woff create mode 100644 CTFd/themes/core/static/fonts/raleway-latin-900italic.woff2 delete mode 100644 CTFd/themes/core/static/js/CTFd.js delete mode 100644 CTFd/themes/core/static/js/challenges.js create mode 100644 CTFd/themes/core/static/js/core.dev.js create mode 100644 CTFd/themes/core/static/js/core.min.js delete mode 100644 CTFd/themes/core/static/js/events.js delete mode 100644 CTFd/themes/core/static/js/ezq.js create mode 100644 CTFd/themes/core/static/js/helpers.dev.js create mode 100644 CTFd/themes/core/static/js/helpers.min.js delete mode 100644 CTFd/themes/core/static/js/hints.js delete mode 100644 CTFd/themes/core/static/js/multi-modal.js create mode 100644 CTFd/themes/core/static/js/pages/challenges.dev.js create mode 100644 CTFd/themes/core/static/js/pages/challenges.min.js create mode 100644 CTFd/themes/core/static/js/pages/main.dev.js create mode 100644 CTFd/themes/core/static/js/pages/main.min.js create mode 100644 CTFd/themes/core/static/js/pages/notifications.dev.js create mode 100644 CTFd/themes/core/static/js/pages/notifications.min.js create mode 100644 CTFd/themes/core/static/js/pages/scoreboard.dev.js create mode 100644 CTFd/themes/core/static/js/pages/scoreboard.min.js create mode 100644 CTFd/themes/core/static/js/pages/settings.dev.js create mode 100644 CTFd/themes/core/static/js/pages/settings.min.js create mode 100644 CTFd/themes/core/static/js/pages/setup.dev.js create mode 100644 CTFd/themes/core/static/js/pages/setup.min.js create mode 100644 CTFd/themes/core/static/js/pages/stats.dev.js create mode 100644 CTFd/themes/core/static/js/pages/stats.min.js create mode 100644 CTFd/themes/core/static/js/pages/teams/private.dev.js create mode 100644 CTFd/themes/core/static/js/pages/teams/private.min.js create mode 100644 CTFd/themes/core/static/js/plotly.bundle.dev.js create mode 100644 CTFd/themes/core/static/js/plotly.bundle.min.js delete mode 100644 CTFd/themes/core/static/js/scoreboard.js delete mode 100644 CTFd/themes/core/static/js/settings.js delete mode 100644 CTFd/themes/core/static/js/style.js delete mode 100644 CTFd/themes/core/static/js/team.js delete mode 100644 CTFd/themes/core/static/js/user.js delete mode 100644 CTFd/themes/core/static/js/utils.js create mode 100644 CTFd/themes/core/static/js/vendor.bundle.dev.js create mode 100644 CTFd/themes/core/static/js/vendor.bundle.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/bootstrap.bundle.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/codemirror.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/eventsource.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/fetch.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/howler.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/jquery.min.js delete mode 100755 CTFd/themes/core/static/js/vendor/markdown-it.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/moment-timezone-with-data.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/moment.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/nunjucks.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/plotly.min.js delete mode 100755 CTFd/themes/core/static/js/vendor/promise-polyfill.min.js delete mode 100644 CTFd/themes/core/static/js/vendor/window-controller.js create mode 100644 CTFd/utils/config/integrations.py create mode 100644 CTFd/utils/formatters/__init__.py create mode 100644 migrations/versions/080d29b15cd3_add_tokens_table.py create mode 100644 tests/api/test_tokens.py create mode 100644 tests/api/v1/teams/test_scoring.py create mode 100644 tests/api/v1/test_tokens.py create mode 100644 tests/api/v1/users/__init__.py create mode 100644 tests/api/v1/users/test_scoring.py create mode 100644 tests/oauth/test_teams.py create mode 100644 tests/test_setup.py create mode 100644 tests/utils/test_formatters.py create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore index 87512692..27f729ae 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,4 @@ CTFd/uploads *.zip # JS -node_modules/ \ No newline at end of file +node_modules/ diff --git a/.travis.yml b/.travis.yml index 0da59c1e..ddc1cf6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ before_install: - python3.6 -m pip install black==19.3b0 install: - pip install -r development.txt + - yarn install --non-interactive - yarn global add prettier@1.17.0 before_script: - psql -c 'create database ctfd;' -U postgres diff --git a/CHANGELOG.md b/CHANGELOG.md index 68fae2f0..673f5454 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,66 @@ +2.2.0 / 2019-12-22 +================== + +## Notice +2.2.0 focuses on updating the front end of CTFd to use more modern programming practices and changes some aspects of core CTFd design. If your current installation is using a custom theme or custom plugin with ***any*** kind of JavaScript, it is likely that you will need to upgrade that theme/plugin to be useable with v2.2.0. + +**General** +* Team size limits can now be enforced from the configuration panel +* Access tokens functionality for API usage +* Admins can now choose how to deliver their notifications + * Toast (new default) + * Alert + * Background + * Sound On / Sound Off +* There is now a notification counter showing how many unread notifications were received +* Setup has been redesigned to have multiple steps + * Added Description + * Added Start time and End time, + * Added MajorLeagueCyber integration + * Added Theme and color selection +* Fixes issue where updating dynamic challenges could change the value to an incorrect value +* Properly use a less restrictive regex to validate email addresses +* Bump Python dependencies to latest working versions +* Admins can now give awards to team members from the team's admin panel page + +**API** +* Team member removals (`DELETE /api/v1/teams/[team_id]/members`) from the admin panel will now delete the removed members's Submissions, Awards, Unlocks + +**Admin Panel** +* Admins can now user a color input box to specify a theme color which is injected as part of the CSS configuration. Theme developers can use this CSS value to change colors and styles accordingly. +* Challenge updates will now alert you if the challenge doesn't have a flag +* Challenge entry now allows you to upload files and enter simple flags from the initial challenge creation page + +**Themes** +* Significant JavaScript and CSS rewrite to use ES6, Webpack, yarn, and babel +* Theme asset specially generated URLs + * Static theme assets are now loaded with either .dev.extension or .min.extension depending on production or development (i.e. debug server) + * Static theme assets are also given a `d` GET parameter that changes per server start. Used to bust browser caches. +* Use `defer` for script tags to not block page rendering +* Only show the MajorLeagueCyber button if configured in configuration +* The admin panel now links to https://help.ctfd.io/ in the top right +* Create an `ezToast()` function to use [Bootstrap's toasts](https://getbootstrap.com/docs/4.3/components/toasts/) +* The user-facing navbar now features icons +* Awards shown on a user's profile can now have award icons +* The default MarkdownIt render created by CTFd will now open links in new tabs +* Country flags can now be shown on the user pages + +**Deployment** +* Switch `Dockerfile` from `python:2.7-alpine` to `python:3.7-alpine` +* Add `SERVER_SENT_EVENTS` config value to control whether Notifications are enabled +* Challenge ID is now recorded in the submission log + +**Plugins** +* Add an endpoint parameter to `register_plugin_assets_directory()` and `register_plugin_asset()` to control what endpoint Flask uses for the added route + +**Miscellaneous** +* `CTFd.utils.email.sendmail()` now allows the caller to specify subject as an argument + * The subject allows for injecting custom variable via the new `CTFd.utils.formatters.safe_format()` function +* Admin user information is now error checked during setup +* Added yarn to the toolchain and the yarn dev, yarn build, yarn verify, and yarn clean scripts +* Prevent old CTFd imports from being imported + + 2.1.5 / 2019-10-2 ================= diff --git a/CTFd/__init__.py b/CTFd/__init__.py index 00cdb61c..da56f945 100644 --- a/CTFd/__init__.py +++ b/CTFd/__init__.py @@ -21,14 +21,16 @@ from CTFd.utils.initialization import ( init_logs, init_events, ) +from CTFd.utils.crypto import sha256 from CTFd.plugins import init_plugins +import datetime # Hack to support Unicode in Python 2 properly if sys.version_info[0] < 3: reload(sys) # noqa: F821 sys.setdefaultencoding("utf-8") -__version__ = "2.1.5" +__version__ = "2.2.0" class CTFdRequest(Request): @@ -50,6 +52,12 @@ class CTFdFlask(Flask): self.jinja_environment = SandboxedBaseEnvironment self.session_interface = CachingSessionInterface(key_prefix="session") self.request_class = CTFdRequest + + # Store server start time + self.start_time = datetime.datetime.utcnow() + + # Create generally unique run identifier + self.run_id = sha256(str(self.start_time))[0:8] Flask.__init__(self, *args, **kwargs) def create_jinja_environment(self): diff --git a/CTFd/admin/statistics.py b/CTFd/admin/statistics.py index 79c867bc..831172a3 100644 --- a/CTFd/admin/statistics.py +++ b/CTFd/admin/statistics.py @@ -2,7 +2,7 @@ from flask import render_template from CTFd.utils.decorators import admins_only from CTFd.utils.updates import update_check from CTFd.utils.modes import get_model -from CTFd.models import db, Solves, Challenges, Fails, Tracking +from CTFd.models import db, Solves, Challenges, Fails, Tracking, Teams, Users from CTFd.admin import admin @@ -13,7 +13,8 @@ def statistics(): Model = get_model() - teams_registered = Model.query.count() + teams_registered = Teams.query.count() + users_registered = Users.query.count() wrong_count = ( Fails.query.join(Model, Fails.account_id == Model.id) @@ -65,6 +66,7 @@ def statistics(): return render_template( "admin/statistics.html", + user_count=users_registered, team_count=teams_registered, ip_count=ip_count, wrong_count=wrong_count, diff --git a/CTFd/api/__init__.py b/CTFd/api/__init__.py index 9eec5e8a..17256e3c 100644 --- a/CTFd/api/__init__.py +++ b/CTFd/api/__init__.py @@ -16,6 +16,7 @@ from CTFd.api.v1.config import configs_namespace from CTFd.api.v1.notifications import notifications_namespace from CTFd.api.v1.pages import pages_namespace from CTFd.api.v1.unlocks import unlocks_namespace +from CTFd.api.v1.tokens import tokens_namespace api = Blueprint("api", __name__, url_prefix="/api/v1") CTFd_API_v1 = Api(api, version="v1", doc=current_app.config.get("SWAGGER_UI")) @@ -35,3 +36,4 @@ CTFd_API_v1.add_namespace(notifications_namespace, "/notifications") CTFd_API_v1.add_namespace(configs_namespace, "/configs") CTFd_API_v1.add_namespace(pages_namespace, "/pages") CTFd_API_v1.add_namespace(unlocks_namespace, "/unlocks") +CTFd_API_v1.add_namespace(tokens_namespace, "/tokens") diff --git a/CTFd/api/v1/challenges.py b/CTFd/api/v1/challenges.py index ce7f09b0..5e7d1e23 100644 --- a/CTFd/api/v1/challenges.py +++ b/CTFd/api/v1/challenges.py @@ -260,13 +260,10 @@ class Challenge(Resource): Model = get_model() if scores_visible() is True and accounts_visible() is True: - solves = ( - Solves.query.join(Model, Solves.account_id == Model.id) - .filter( - Solves.challenge_id == chal.id, - Model.banned == False, - Model.hidden == False, - ) + solves = Solves.query.join(Model, Solves.account_id == Model.id).filter( + Solves.challenge_id == chal.id, + Model.banned == False, + Model.hidden == False, ) # Only show solves that happened before freeze time if configured @@ -391,8 +388,9 @@ class ChallengeAttempt(Resource): ) log( "submissions", - "[{date}] {name} submitted {submission} with kpm {kpm} [TOO FAST]", + "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [TOO FAST]", submission=request_data["submission"].encode("utf-8"), + challenge_id=challenge_id, kpm=kpm, ) # Submitting too fast @@ -437,8 +435,9 @@ class ChallengeAttempt(Resource): log( "submissions", - "[{date}] {name} submitted {submission} with kpm {kpm} [CORRECT]", + "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [CORRECT]", submission=request_data["submission"].encode("utf-8"), + challenge_id=challenge_id, kpm=kpm, ) return { @@ -454,8 +453,9 @@ class ChallengeAttempt(Resource): log( "submissions", - "[{date}] {name} submitted {submission} with kpm {kpm} [WRONG]", + "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [WRONG]", submission=request_data["submission"].encode("utf-8"), + challenge_id=challenge_id, kpm=kpm, ) @@ -487,8 +487,9 @@ class ChallengeAttempt(Resource): else: log( "submissions", - "[{date}] {name} submitted {submission} with kpm {kpm} [ALREADY SOLVED]", + "[{date}] {name} submitted {submission} on {challenge_id} with kpm {kpm} [ALREADY SOLVED]", submission=request_data["submission"].encode("utf-8"), + challenge_id=challenge_id, kpm=kpm, ) return { diff --git a/CTFd/api/v1/notifications.py b/CTFd/api/v1/notifications.py index 5f8419e1..f3dffc1b 100644 --- a/CTFd/api/v1/notifications.py +++ b/CTFd/api/v1/notifications.py @@ -34,6 +34,13 @@ class NotificantionList(Resource): db.session.commit() response = schema.dump(result.data) + + # Grab additional settings + notif_type = req.get("type", "alert") + notif_sound = req.get("sound", True) + response.data["type"] = notif_type + response.data["sound"] = notif_sound + current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} diff --git a/CTFd/api/v1/teams.py b/CTFd/api/v1/teams.py index 2e471aa1..6e579dc4 100644 --- a/CTFd/api/v1/teams.py +++ b/CTFd/api/v1/teams.py @@ -1,6 +1,6 @@ from flask import session, request, abort from flask_restplus import Namespace, Resource -from CTFd.models import db, Users, Teams +from CTFd.models import db, Users, Teams, Submissions, Awards, Unlocks from CTFd.schemas.teams import TeamSchema from CTFd.schemas.submissions import SubmissionSchema from CTFd.schemas.awards import AwardSchema @@ -68,6 +68,8 @@ class TeamPublic(Resource): if response.errors: return {"success": False, "errors": response.errors}, 400 + response.data["place"] = team.place + response.data["score"] = team.score return {"success": True, "data": response.data} @admins_only @@ -118,6 +120,8 @@ class TeamPrivate(Resource): if response.errors: return {"success": False, "errors": response.errors}, 400 + response.data["place"] = team.place + response.data["score"] = team.score return {"success": True, "data": response.data} @authed_only @@ -206,6 +210,12 @@ class TeamMembers(Resource): if user.team_id == team.id: team.members.remove(user) + + # Remove information that links the user to the team + Submissions.query.filter_by(user_id=user.id).delete() + Awards.query.filter_by(user_id=user.id).delete() + Unlocks.query.filter_by(user_id=user.id).delete() + db.session.commit() else: return ( diff --git a/CTFd/api/v1/tokens.py b/CTFd/api/v1/tokens.py new file mode 100644 index 00000000..8e2b4b51 --- /dev/null +++ b/CTFd/api/v1/tokens.py @@ -0,0 +1,83 @@ +from flask import request, session +from flask_restplus import Namespace, Resource +from CTFd.models import db, Tokens +from CTFd.utils.user import get_current_user, is_admin +from CTFd.schemas.tokens import TokenSchema +from CTFd.utils.security.auth import generate_user_token +from CTFd.utils.decorators import require_verified_emails, authed_only +import datetime + +tokens_namespace = Namespace("tokens", description="Endpoint to retrieve Tokens") + + +@tokens_namespace.route("") +class TokenList(Resource): + @require_verified_emails + @authed_only + def get(self): + user = get_current_user() + tokens = Tokens.query.filter_by(user_id=user.id) + response = TokenSchema(view=["id", "type", "expiration"], many=True).dump( + tokens + ) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @require_verified_emails + @authed_only + def post(self): + req = request.get_json() + expiration = req.get("expiration") + if expiration: + expiration = datetime.datetime.strptime(expiration, "%Y-%m-%d") + + user = get_current_user() + token = generate_user_token(user, expiration=expiration) + + # Explicitly use admin view so that user's can see the value of their token + schema = TokenSchema(view="admin") + response = schema.dump(token) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + +@tokens_namespace.route("/") +@tokens_namespace.param("token_id", "A Token ID") +class TokenDetail(Resource): + @require_verified_emails + @authed_only + def get(self, token_id): + if is_admin(): + token = Tokens.query.filter_by(id=token_id).first_or_404() + else: + token = Tokens.query.filter_by( + id=token_id, user_id=session["id"] + ).first_or_404() + + schema = TokenSchema(view=session.get("type", "user")) + response = schema.dump(token) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @require_verified_emails + @authed_only + def delete(self, token_id): + if is_admin(): + token = Tokens.query.filter_by(id=token_id).first_or_404() + else: + user = get_current_user() + token = Tokens.query.filter_by(id=token_id, user_id=user.id).first_or_404() + db.session.delete(token) + db.session.commit() + db.session.close() + + return {"success": True} diff --git a/CTFd/auth.py b/CTFd/auth.py index bc4eed9a..5ca6d52e 100644 --- a/CTFd/auth.py +++ b/CTFd/auth.py @@ -400,6 +400,15 @@ def oauth_redirect(): db.session.add(team) db.session.commit() + team_size_limit = get_config("team_size", default=0) + if team_size_limit and len(team.members) >= team_size_limit: + plural = "" if team_size_limit == 1 else "s" + size_error = "Teams are limited to {limit} member{plural}.".format( + limit=team_size_limit, plural=plural + ) + error_for(endpoint="auth.login", message=size_error) + return redirect(url_for("auth.login")) + team.members.append(user) db.session.commit() diff --git a/CTFd/config.py b/CTFd/config.py index 5370cdae..47b1edfc 100644 --- a/CTFd/config.py +++ b/CTFd/config.py @@ -228,6 +228,9 @@ class Config(object): APPLICATION_ROOT: Specifies what path CTFd is mounted under. It can be used to run CTFd in a subdirectory. Example: /ctfd + + SERVER_SENT_EVENTS: + Specifies whether or not to enable to server-sent events based Notifications system. """ REVERSE_PROXY = os.getenv("REVERSE_PROXY") or False TEMPLATES_AUTO_RELOAD = not os.getenv("TEMPLATES_AUTO_RELOAD") # Defaults True @@ -237,6 +240,7 @@ class Config(object): SWAGGER_UI = "/" if os.getenv("SWAGGER_UI") is not None else False # Defaults False UPDATE_CHECK = not os.getenv("UPDATE_CHECK") # Defaults True APPLICATION_ROOT = os.getenv("APPLICATION_ROOT") or "/" + SERVER_SENT_EVENTS = not os.getenv("SERVER_SENT_EVENTS") # Defaults True """ === OAUTH === diff --git a/CTFd/events/__init__.py b/CTFd/events/__init__.py index 7fad9199..09a22625 100644 --- a/CTFd/events/__init__.py +++ b/CTFd/events/__init__.py @@ -1,4 +1,5 @@ from flask import current_app, Blueprint, Response, stream_with_context +from CTFd.utils import get_app_config from CTFd.utils.decorators import authed_only, ratelimit events = Blueprint("events", __name__) @@ -13,4 +14,8 @@ def subscribe(): for event in current_app.events_manager.subscribe(): yield str(event) + enabled = get_app_config("SERVER_SENT_EVENTS") + if enabled is False: + return ("", 204) + return Response(gen(), mimetype="text/event-stream") diff --git a/CTFd/exceptions/__init__.py b/CTFd/exceptions/__init__.py new file mode 100644 index 00000000..55030cf0 --- /dev/null +++ b/CTFd/exceptions/__init__.py @@ -0,0 +1,6 @@ +class UserNotFoundException(Exception): + pass + + +class UserTokenExpiredException(Exception): + pass diff --git a/CTFd/models/__init__.py b/CTFd/models/__init__.py index 5dee907a..99c9ec0c 100644 --- a/CTFd/models/__init__.py +++ b/CTFd/models/__init__.py @@ -281,7 +281,12 @@ class Users(db.Model): @property def place(self): - return self.get_place(admin=False) + from CTFd.utils.config.visibility import scores_visible + + if scores_visible(): + return self.get_place(admin=False) + else: + return None def get_solves(self, admin=False): solves = Solves.query.filter_by(user_id=self.id) @@ -417,7 +422,12 @@ class Teams(db.Model): @property def place(self): - return self.get_place(admin=False) + from CTFd.utils.config.visibility import scores_visible + + if scores_visible(): + return self.get_place(admin=False) + else: + return None def get_solves(self, admin=False): member_ids = [member.id for member in self.members] @@ -631,6 +641,33 @@ class Configs(db.Model): super(Configs, self).__init__(**kwargs) +class Tokens(db.Model): + __tablename__ = "tokens" + id = db.Column(db.Integer, primary_key=True) + type = db.Column(db.String(32)) + user_id = db.Column(db.Integer, db.ForeignKey("users.id", ondelete="CASCADE")) + created = db.Column(db.DateTime, default=datetime.datetime.utcnow) + expiration = db.Column( + db.DateTime, + default=lambda: datetime.datetime.utcnow() + datetime.timedelta(days=30), + ) + value = db.Column(db.String(128), unique=True) + + user = db.relationship("Users", foreign_keys="Tokens.user_id", lazy="select") + + __mapper_args__ = {"polymorphic_on": type} + + def __init__(self, *args, **kwargs): + super(Tokens, self).__init__(**kwargs) + + def __repr__(self): + return "" % self.id + + +class UserTokens(Tokens): + __mapper_args__ = {"polymorphic_identity": "user"} + + @cache.memoize() def get_config(key): """ diff --git a/CTFd/plugins/__init__.py b/CTFd/plugins/__init__.py index 2ebb278d..dca1f8f9 100644 --- a/CTFd/plugins/__init__.py +++ b/CTFd/plugins/__init__.py @@ -18,7 +18,7 @@ from CTFd.utils.config.pages import get_pages Menu = namedtuple("Menu", ["title", "route"]) -def register_plugin_assets_directory(app, base_path, admins_only=False): +def register_plugin_assets_directory(app, base_path, admins_only=False, endpoint=None): """ Registers a directory to serve assets @@ -28,15 +28,17 @@ def register_plugin_assets_directory(app, base_path, admins_only=False): :return: """ base_path = base_path.strip("/") + if endpoint is None: + endpoint = base_path.replace("/", ".") def assets_handler(path): return send_from_directory(base_path, path) rule = "/" + base_path + "/" - app.add_url_rule(rule=rule, endpoint=base_path, view_func=assets_handler) + app.add_url_rule(rule=rule, endpoint=endpoint, view_func=assets_handler) -def register_plugin_asset(app, asset_path, admins_only=False): +def register_plugin_asset(app, asset_path, admins_only=False, endpoint=None): """ Registers an file path to be served by CTFd @@ -46,6 +48,8 @@ def register_plugin_asset(app, asset_path, admins_only=False): :return: """ asset_path = asset_path.strip("/") + if endpoint is None: + endpoint = asset_path.replace("/", ".") def asset_handler(): return send_file(asset_path) @@ -53,7 +57,7 @@ def register_plugin_asset(app, asset_path, admins_only=False): if admins_only: asset_handler = admins_only_wrapper(asset_handler) rule = "/" + asset_path - app.add_url_rule(rule=rule, endpoint=asset_path, view_func=asset_handler) + app.add_url_rule(rule=rule, endpoint=endpoint, view_func=asset_handler) def override_template(*args, **kwargs): diff --git a/CTFd/plugins/challenges/assets/create.html b/CTFd/plugins/challenges/assets/create.html index 79c72a91..edca7c58 100644 --- a/CTFd/plugins/challenges/assets/create.html +++ b/CTFd/plugins/challenges/assets/create.html @@ -1,7 +1,7 @@