diff --git a/CTFd/constants/config.py b/CTFd/constants/config.py index b9171cfc..37372240 100644 --- a/CTFd/constants/config.py +++ b/CTFd/constants/config.py @@ -1,7 +1,42 @@ import json +from CTFd.constants import JinjaEnum, RawEnum from CTFd.utils import get_config -from CTFd.utils.helpers import markup + + +class ConfigTypes(str, RawEnum): + CHALLENGE_VISIBILITY = "challenge_visibility" + SCORE_VISIBILITY = "score_visibility" + ACCOUNT_VISIBILITY = "account_visibility" + REGISTRATION_VISIBILITY = "registration_visibility" + + +@JinjaEnum +class ChallengeVisibilityTypes(str, RawEnum): + PUBLIC = "public" + PRIVATE = "private" + ADMINS = "admins" + + +@JinjaEnum +class ScoreVisibilityTypes(str, RawEnum): + PUBLIC = "public" + PRIVATE = "private" + HIDDEN = "hidden" + ADMINS = "admins" + + +@JinjaEnum +class AccountVisibilityTypes(str, RawEnum): + PUBLIC = "public" + PRIVATE = "private" + ADMINS = "admins" + + +@JinjaEnum +class RegistrationVisibilityTypes(str, RawEnum): + PUBLIC = "public" + PRIVATE = "private" class _ConfigsWrapper: @@ -14,10 +49,14 @@ class _ConfigsWrapper: @property def theme_header(self): + from CTFd.utils.helpers import markup + return markup(get_config("theme_header", default="")) @property def theme_footer(self): + from CTFd.utils.helpers import markup + return markup(get_config("theme_footer", default="")) @property diff --git a/CTFd/utils/__init__.py b/CTFd/utils/__init__.py index b8401e5e..39e36d15 100644 --- a/CTFd/utils/__init__.py +++ b/CTFd/utils/__init__.py @@ -1,3 +1,5 @@ +from enum import Enum + import cmarkgfm from flask import current_app as app @@ -43,6 +45,10 @@ def _get_config(key): def get_config(key, default=None): + # Convert enums to raw string values to cache better + if isinstance(key, Enum): + key = str(key) + value = _get_config(key) if value is KeyError: return default @@ -58,5 +64,10 @@ def set_config(key, value): config = Configs(key=key, value=value) db.session.add(config) db.session.commit() + + # Convert enums to raw string values to cache better + if isinstance(key, Enum): + key = str(key) + cache.delete_memoized(_get_config, key) return config diff --git a/CTFd/utils/config/visibility.py b/CTFd/utils/config/visibility.py index f9337978..73d0145d 100644 --- a/CTFd/utils/config/visibility.py +++ b/CTFd/utils/config/visibility.py @@ -1,44 +1,51 @@ +from CTFd.constants.config import ( + AccountVisibilityTypes, + ChallengeVisibilityTypes, + ConfigTypes, + RegistrationVisibilityTypes, + ScoreVisibilityTypes, +) from CTFd.utils import get_config from CTFd.utils.user import authed, is_admin def challenges_visible(): - v = get_config("challenge_visibility") - if v == "public": + v = get_config(ConfigTypes.CHALLENGE_VISIBILITY) + if v == ChallengeVisibilityTypes.PUBLIC: return True - elif v == "private": + elif v == ChallengeVisibilityTypes.PRIVATE: return authed() - elif v == "admins": + elif v == ChallengeVisibilityTypes.ADMINS: return is_admin() def scores_visible(): - v = get_config("score_visibility") - if v == "public": + v = get_config(ConfigTypes.SCORE_VISIBILITY) + if v == ScoreVisibilityTypes.PUBLIC: return True - elif v == "private": + elif v == ScoreVisibilityTypes.PRIVATE: return authed() - elif v == "hidden": + elif v == ScoreVisibilityTypes.HIDDEN: return False - elif v == "admins": + elif v == ScoreVisibilityTypes.ADMINS: return is_admin() def accounts_visible(): - v = get_config("account_visibility") - if v == "public": + v = get_config(ConfigTypes.ACCOUNT_VISIBILITY) + if v == AccountVisibilityTypes.PUBLIC: return True - elif v == "private": + elif v == AccountVisibilityTypes.PRIVATE: return authed() - elif v == "admins": + elif v == AccountVisibilityTypes.ADMINS: return is_admin() def registration_visible(): - v = get_config("registration_visibility") - if v == "public": + v = get_config(ConfigTypes.REGISTRATION_VISIBILITY) + if v == RegistrationVisibilityTypes.PUBLIC: return True - elif v == "private": + elif v == RegistrationVisibilityTypes.PRIVATE: return False else: return False diff --git a/CTFd/utils/decorators/visibility.py b/CTFd/utils/decorators/visibility.py index 31c26c92..dea89e0d 100644 --- a/CTFd/utils/decorators/visibility.py +++ b/CTFd/utils/decorators/visibility.py @@ -2,6 +2,13 @@ import functools from flask import abort, redirect, render_template, request, url_for +from CTFd.constants.config import ( + AccountVisibilityTypes, + ChallengeVisibilityTypes, + ConfigTypes, + RegistrationVisibilityTypes, + ScoreVisibilityTypes, +) from CTFd.utils import get_config from CTFd.utils.user import authed, is_admin @@ -9,11 +16,11 @@ from CTFd.utils.user import authed, is_admin def check_score_visibility(f): @functools.wraps(f) def _check_score_visibility(*args, **kwargs): - v = get_config("score_visibility") - if v == "public": + v = get_config(ConfigTypes.SCORE_VISIBILITY) + if v == ScoreVisibilityTypes.PUBLIC: return f(*args, **kwargs) - elif v == "private": + elif v == ScoreVisibilityTypes.PRIVATE: if authed(): return f(*args, **kwargs) else: @@ -22,13 +29,13 @@ def check_score_visibility(f): else: return redirect(url_for("auth.login", next=request.full_path)) - elif v == "hidden": + elif v == ScoreVisibilityTypes.HIDDEN: return ( render_template("errors/403.html", error="Scores are currently hidden"), 403, ) - elif v == "admins": + elif v == ScoreVisibilityTypes.ADMINS: if is_admin(): return f(*args, **kwargs) else: @@ -40,11 +47,11 @@ def check_score_visibility(f): def check_challenge_visibility(f): @functools.wraps(f) def _check_challenge_visibility(*args, **kwargs): - v = get_config("challenge_visibility") - if v == "public": + v = get_config(ConfigTypes.CHALLENGE_VISIBILITY) + if v == ChallengeVisibilityTypes.PUBLIC: return f(*args, **kwargs) - elif v == "private": + elif v == ChallengeVisibilityTypes.PRIVATE: if authed(): return f(*args, **kwargs) else: @@ -53,7 +60,7 @@ def check_challenge_visibility(f): else: return redirect(url_for("auth.login", next=request.full_path)) - elif v == "admins": + elif v == ChallengeVisibilityTypes.ADMINS: if is_admin(): return f(*args, **kwargs) else: @@ -68,11 +75,11 @@ def check_challenge_visibility(f): def check_account_visibility(f): @functools.wraps(f) def _check_account_visibility(*args, **kwargs): - v = get_config("account_visibility") - if v == "public": + v = get_config(ConfigTypes.ACCOUNT_VISIBILITY) + if v == AccountVisibilityTypes.PUBLIC: return f(*args, **kwargs) - elif v == "private": + elif v == AccountVisibilityTypes.PRIVATE: if authed(): return f(*args, **kwargs) else: @@ -81,7 +88,7 @@ def check_account_visibility(f): else: return redirect(url_for("auth.login", next=request.full_path)) - elif v == "admins": + elif v == AccountVisibilityTypes.ADMINS: if is_admin(): return f(*args, **kwargs) else: @@ -93,10 +100,10 @@ def check_account_visibility(f): def check_registration_visibility(f): @functools.wraps(f) def _check_registration_visibility(*args, **kwargs): - v = get_config("registration_visibility") - if v == "public": + v = get_config(ConfigTypes.REGISTRATION_VISIBILITY) + if v == RegistrationVisibilityTypes.PUBLIC: return f(*args, **kwargs) - elif v == "private": + elif v == RegistrationVisibilityTypes.PRIVATE: abort(404) return _check_registration_visibility diff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py index ff114d53..b8247034 100644 --- a/CTFd/utils/initialization/__init__.py +++ b/CTFd/utils/initialization/__init__.py @@ -20,12 +20,6 @@ from CTFd.utils.config import ( is_setup, ) from CTFd.utils.config.pages import get_pages -from CTFd.utils.config.visibility import ( - accounts_visible, - challenges_visible, - registration_visible, - scores_visible, -) from CTFd.utils.countries import get_countries, lookup_country_code from CTFd.utils.dates import isoformat, unix_time, unix_time_millis from CTFd.utils.events import EventManager, RedisEventManager @@ -61,6 +55,12 @@ def init_template_globals(app): from CTFd.constants.plugins import Plugins from CTFd.constants.sessions import Session from CTFd.forms import Forms + from CTFd.utils.config.visibility import ( + accounts_visible, + challenges_visible, + registration_visible, + scores_visible, + ) app.jinja_env.globals.update(config=config) app.jinja_env.globals.update(get_pages=get_pages) diff --git a/CTFd/views.py b/CTFd/views.py index c7a26c30..83782580 100644 --- a/CTFd/views.py +++ b/CTFd/views.py @@ -7,6 +7,13 @@ from flask.helpers import safe_join from sqlalchemy.exc import IntegrityError from CTFd.cache import cache +from CTFd.constants.config import ( + AccountVisibilityTypes, + ChallengeVisibilityTypes, + ConfigTypes, + RegistrationVisibilityTypes, + ScoreVisibilityTypes, +) from CTFd.models import ( Admins, Files, @@ -156,10 +163,14 @@ def setup(): page = Pages(title=None, route="index", content=index, draft=False) # Visibility - set_config("challenge_visibility", "private") - set_config("registration_visibility", "public") - set_config("score_visibility", "public") - set_config("account_visibility", "public") + set_config( + ConfigTypes.CHALLENGE_VISIBILITY, ChallengeVisibilityTypes.PRIVATE + ) + set_config( + ConfigTypes.REGISTRATION_VISIBILITY, RegistrationVisibilityTypes.PUBLIC + ) + set_config(ConfigTypes.SCORE_VISIBILITY, ScoreVisibilityTypes.PUBLIC) + set_config(ConfigTypes.ACCOUNT_VISIBILITY, AccountVisibilityTypes.PUBLIC) # Verify emails set_config("verify_emails", None) @@ -354,7 +365,7 @@ def files(path): # Check user is admin if challenge_visibility is admins only if ( - get_config("challenge_visibility") == "admins" + get_config(ConfigTypes.CHALLENGE_VISIBILITY) == "admins" and user.type != "admin" ): abort(403)