Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into UserChallenge_plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
iimog committed Jan 10, 2025
2 parents 7909127 + 3895558 commit 206ed89
Show file tree
Hide file tree
Showing 44 changed files with 488 additions and 125 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
python -m pip install --upgrade pip
python -m pip install -r linting.txt
sudo yarn --cwd CTFd/themes/admin install --non-interactive
sudo yarn global add prettier@^3.2.5
sudo yarn global add [email protected]
- name: Lint
run: make lint
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/verify-themes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Theme Verification

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
build:

runs-on: ubuntu-latest

name: Theme Verification
steps:
- uses: actions/checkout@v2

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Verify admin theme
run: |
pwd
yarn install --non-interactive
yarn verify
working-directory: ./CTFd/themes/admin

# TODO: Replace in 4.0 with deprecation of previous core theme
- name: Verify core-beta theme
run: |
pwd
yarn install --non-interactive
yarn verify
working-directory: ./CTFd/themes/core-beta
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
# 3.7.5 / 2024-12-27

**Security**

- Change confirmation and reset password emails to be single use instead of only expiring in 30 minutes

**General**

- Fix issue where users could set their own bracket after registration
- If a user or team do not have a password set we allow setting a password without providing a previous password confirmation
- Fix issue where dynamic challenges did not return their attribution over the API
- Language selection is now available in the main theme navigation bar

**Admin Panel**

- A point breakdown graph showing the amount of challenge points allocated to each category has been added to the Admin Panel
- Bracket ID and Bracket Name have been added to CSV scoreboard exports
- Fix issue with certain interactions in the Media Library

**API**

- Swagger specification has been updated to properly validate
- `/api/v1/flags/types` and `/api/v1/flags/types/<type_name>` have been seperated into two seperate controllers

**Deployment**

- IP Tracking has been updated to only occur if we have not seen the IP before or on state changing methods
- Bump dependencies for `cmarkgfm` and `jinja2`

# 3.7.4 / 2024-10-08

**Security**
Expand Down
2 changes: 1 addition & 1 deletion CTFd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from CTFd.utils.updates import update_check
from CTFd.utils.user import get_locale

__version__ = "3.7.4"
__version__ = "3.7.5"
__channel__ = "oss"


Expand Down
38 changes: 20 additions & 18 deletions CTFd/auth.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import base64 # noqa: I001

import requests
from flask import Blueprint, abort
from flask import current_app as app
from flask import redirect, render_template, request, session, url_for
from itsdangerous.exc import BadSignature, BadTimeSignature, SignatureExpired

from CTFd.cache import clear_team_session, clear_user_session
from CTFd.exceptions.email import (
UserConfirmTokenInvalidException,
UserResetPasswordTokenInvalidException,
)
from CTFd.models import Brackets, Teams, UserFieldEntries, UserFields, Users, db
from CTFd.utils import config, email, get_app_config, get_config
from CTFd.utils import user as current_user
Expand All @@ -21,7 +22,12 @@
from CTFd.utils.logging import log
from CTFd.utils.modes import TEAMS_MODE
from CTFd.utils.security.auth import login_user, logout_user
from CTFd.utils.security.signing import unserialize
from CTFd.utils.security.email import (
remove_email_confirm_token,
remove_reset_password_token,
verify_email_confirm_token,
verify_reset_password_token,
)
from CTFd.utils.validators import ValidationError

auth = Blueprint("auth", __name__)
Expand All @@ -38,14 +44,11 @@ def confirm(data=None):
# User is confirming email account
if data and request.method == "GET":
try:
user_email = unserialize(data, max_age=1800)
except (BadTimeSignature, SignatureExpired):
return render_template(
"confirm.html", errors=["Your confirmation link has expired"]
)
except (BadSignature, TypeError, base64.binascii.Error):
user_email = verify_email_confirm_token(data)
except (UserConfirmTokenInvalidException):
return render_template(
"confirm.html", errors=["Your confirmation token is invalid"]
"confirm.html",
errors=["Your confirmation link is invalid, please generate a new one"],
)

user = Users.query.filter_by(email=user_email).first_or_404()
Expand All @@ -59,6 +62,7 @@ def confirm(data=None):
name=user.name,
)
db.session.commit()
remove_email_confirm_token(data)
clear_user_session(user_id=user.id)
email.successful_registration_notification(user.email)
db.session.close()
Expand Down Expand Up @@ -107,14 +111,11 @@ def reset_password(data=None):

if data is not None:
try:
email_address = unserialize(data, max_age=1800)
except (BadTimeSignature, SignatureExpired):
email_address = verify_reset_password_token(data)
except (UserResetPasswordTokenInvalidException):
return render_template(
"reset_password.html", errors=["Your link has expired"]
)
except (BadSignature, TypeError, base64.binascii.Error):
return render_template(
"reset_password.html", errors=["Your reset token is invalid"]
"reset_password.html",
errors=["Your reset link is invalid, please generate a new one"],
)

if request.method == "GET":
Expand All @@ -138,6 +139,7 @@ def reset_password(data=None):

user.password = password
db.session.commit()
remove_reset_password_token(data)
clear_user_session(user_id=user.id)
log(
"logins",
Expand Down
3 changes: 3 additions & 0 deletions CTFd/constants/languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ class Languages(str, RawEnum):
SELECT_LANGUAGE_LIST = [("", "")] + [
(str(lang), LANGUAGE_NAMES.get(str(lang))) for lang in Languages
]

Languages.names = LANGUAGE_NAMES
Languages.select_list = SELECT_LANGUAGE_LIST
6 changes: 6 additions & 0 deletions CTFd/exceptions/email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class UserConfirmTokenInvalidException(Exception):
pass


class UserResetPasswordTokenInvalidException(Exception):
pass
2 changes: 2 additions & 0 deletions CTFd/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class _FormsWrapper:
from CTFd.forms import submissions # noqa: I001 isort:skip
from CTFd.forms import users # noqa: I001 isort:skip
from CTFd.forms import challenges # noqa: I001 isort:skip
from CTFd.forms import language # noqa: I001 isort:skip
from CTFd.forms import notifications # noqa: I001 isort:skip
from CTFd.forms import config # noqa: I001 isort:skip
from CTFd.forms import pages # noqa: I001 isort:skip
Expand All @@ -42,6 +43,7 @@ class _FormsWrapper:
Forms.submissions = submissions
Forms.users = users
Forms.challenges = challenges
Forms.language = language
Forms.notifications = notifications
Forms.config = config
Forms.pages = pages
Expand Down
19 changes: 19 additions & 0 deletions CTFd/forms/language.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from wtforms import RadioField

from CTFd.constants.languages import LANGUAGE_NAMES
from CTFd.forms import BaseForm


def LanguageForm(*args, **kwargs):
from CTFd.utils.user import get_locale

class _LanguageForm(BaseForm):
"""Language form for only switching langauge without rendering all profile settings"""

language = RadioField(
"",
choices=LANGUAGE_NAMES.items(),
default=get_locale(),
)

return _LanguageForm(*args, **kwargs)
20 changes: 10 additions & 10 deletions CTFd/themes/admin/assets/js/components/files/MediaLibrary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export default {
return CTFd.config.urlRoot + "/files/" + this.selectedFile.location;
},
deleteSelectedFile: function () {
var file_id = this.selectedFile.id;
const file_id = this.selectedFile.id;
if (confirm("Are you sure you want to delete this file?")) {
CTFd.fetch("/api/v1/files/" + file_id, {
Expand All @@ -276,26 +276,26 @@ export default {
if (editor.hasOwnProperty("codemirror")) {
editor = editor.codemirror;
}
let doc = editor.getDoc();
let cursor = doc.getCursor();
const doc = editor.getDoc();
const cursor = doc.getCursor();
let url = this.buildSelectedFileUrl();
let img =
const url = this.buildSelectedFileUrl();
const img =
this.getIconClass(this.selectedFile.location) === "far fa-file-image";
let filename = url.split("/").pop();
link = "[{0}]({1})".format(filename, url);
const filename = url.split("/").pop();
let link = "[{0}]({1})".format(filename, url);
if (img) {
link = "!" + link;
}
doc.replaceRange(link, cursor);
},
downloadSelectedFile: function () {
var link = this.buildSelectedFileUrl();
const link = this.buildSelectedFileUrl();
window.open(link, "_blank");
},
getIconClass: function (filename) {
var mapping = {
const mapping = {
// Image Files
png: "far fa-file-image",
jpg: "far fa-file-image",
Expand Down Expand Up @@ -340,7 +340,7 @@ export default {
go: "far fa-file-code",
};
var ext = filename.split(".").pop();
const ext = filename.split(".").pop();
return mapping[ext] || "far fa-file";
},
},
Expand Down
2 changes: 1 addition & 1 deletion CTFd/themes/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@vitejs/plugin-vue": "^5.1.4",
"@vue/compiler-sfc": "^3.3.13",
"eslint": "^8.56.0",
"prettier": "^3.2.5",
"prettier": "3.2.5",
"rollup-plugin-copy": "^3.5.0",
"sass": "^1.70.0",
"vite": "^5.1.2"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 206ed89

Please sign in to comment.