929 better visibility constants (#1490)

* Adds Enums for visibility configs for better reusability. Old tests will continue to use the static values but new ones should use the enum values. 
* Closes #929
This commit is contained in:
Kevin Chung
2020-06-14 04:15:55 -04:00
committed by GitHub
parent bacb8977a4
commit 1143d751c8
6 changed files with 119 additions and 44 deletions

View File

@@ -1,7 +1,42 @@
import json import json
from CTFd.constants import JinjaEnum, RawEnum
from CTFd.utils import get_config 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: class _ConfigsWrapper:
@@ -14,10 +49,14 @@ class _ConfigsWrapper:
@property @property
def theme_header(self): def theme_header(self):
from CTFd.utils.helpers import markup
return markup(get_config("theme_header", default="")) return markup(get_config("theme_header", default=""))
@property @property
def theme_footer(self): def theme_footer(self):
from CTFd.utils.helpers import markup
return markup(get_config("theme_footer", default="")) return markup(get_config("theme_footer", default=""))
@property @property

View File

@@ -1,3 +1,5 @@
from enum import Enum
import cmarkgfm import cmarkgfm
from flask import current_app as app from flask import current_app as app
@@ -43,6 +45,10 @@ def _get_config(key):
def get_config(key, default=None): 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) value = _get_config(key)
if value is KeyError: if value is KeyError:
return default return default
@@ -58,5 +64,10 @@ def set_config(key, value):
config = Configs(key=key, value=value) config = Configs(key=key, value=value)
db.session.add(config) db.session.add(config)
db.session.commit() 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) cache.delete_memoized(_get_config, key)
return config return config

View File

@@ -1,44 +1,51 @@
from CTFd.constants.config import (
AccountVisibilityTypes,
ChallengeVisibilityTypes,
ConfigTypes,
RegistrationVisibilityTypes,
ScoreVisibilityTypes,
)
from CTFd.utils import get_config from CTFd.utils import get_config
from CTFd.utils.user import authed, is_admin from CTFd.utils.user import authed, is_admin
def challenges_visible(): def challenges_visible():
v = get_config("challenge_visibility") v = get_config(ConfigTypes.CHALLENGE_VISIBILITY)
if v == "public": if v == ChallengeVisibilityTypes.PUBLIC:
return True return True
elif v == "private": elif v == ChallengeVisibilityTypes.PRIVATE:
return authed() return authed()
elif v == "admins": elif v == ChallengeVisibilityTypes.ADMINS:
return is_admin() return is_admin()
def scores_visible(): def scores_visible():
v = get_config("score_visibility") v = get_config(ConfigTypes.SCORE_VISIBILITY)
if v == "public": if v == ScoreVisibilityTypes.PUBLIC:
return True return True
elif v == "private": elif v == ScoreVisibilityTypes.PRIVATE:
return authed() return authed()
elif v == "hidden": elif v == ScoreVisibilityTypes.HIDDEN:
return False return False
elif v == "admins": elif v == ScoreVisibilityTypes.ADMINS:
return is_admin() return is_admin()
def accounts_visible(): def accounts_visible():
v = get_config("account_visibility") v = get_config(ConfigTypes.ACCOUNT_VISIBILITY)
if v == "public": if v == AccountVisibilityTypes.PUBLIC:
return True return True
elif v == "private": elif v == AccountVisibilityTypes.PRIVATE:
return authed() return authed()
elif v == "admins": elif v == AccountVisibilityTypes.ADMINS:
return is_admin() return is_admin()
def registration_visible(): def registration_visible():
v = get_config("registration_visibility") v = get_config(ConfigTypes.REGISTRATION_VISIBILITY)
if v == "public": if v == RegistrationVisibilityTypes.PUBLIC:
return True return True
elif v == "private": elif v == RegistrationVisibilityTypes.PRIVATE:
return False return False
else: else:
return False return False

View File

@@ -2,6 +2,13 @@ import functools
from flask import abort, redirect, render_template, request, url_for 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 import get_config
from CTFd.utils.user import authed, is_admin 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): def check_score_visibility(f):
@functools.wraps(f) @functools.wraps(f)
def _check_score_visibility(*args, **kwargs): def _check_score_visibility(*args, **kwargs):
v = get_config("score_visibility") v = get_config(ConfigTypes.SCORE_VISIBILITY)
if v == "public": if v == ScoreVisibilityTypes.PUBLIC:
return f(*args, **kwargs) return f(*args, **kwargs)
elif v == "private": elif v == ScoreVisibilityTypes.PRIVATE:
if authed(): if authed():
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
@@ -22,13 +29,13 @@ def check_score_visibility(f):
else: else:
return redirect(url_for("auth.login", next=request.full_path)) return redirect(url_for("auth.login", next=request.full_path))
elif v == "hidden": elif v == ScoreVisibilityTypes.HIDDEN:
return ( return (
render_template("errors/403.html", error="Scores are currently hidden"), render_template("errors/403.html", error="Scores are currently hidden"),
403, 403,
) )
elif v == "admins": elif v == ScoreVisibilityTypes.ADMINS:
if is_admin(): if is_admin():
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
@@ -40,11 +47,11 @@ def check_score_visibility(f):
def check_challenge_visibility(f): def check_challenge_visibility(f):
@functools.wraps(f) @functools.wraps(f)
def _check_challenge_visibility(*args, **kwargs): def _check_challenge_visibility(*args, **kwargs):
v = get_config("challenge_visibility") v = get_config(ConfigTypes.CHALLENGE_VISIBILITY)
if v == "public": if v == ChallengeVisibilityTypes.PUBLIC:
return f(*args, **kwargs) return f(*args, **kwargs)
elif v == "private": elif v == ChallengeVisibilityTypes.PRIVATE:
if authed(): if authed():
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
@@ -53,7 +60,7 @@ def check_challenge_visibility(f):
else: else:
return redirect(url_for("auth.login", next=request.full_path)) return redirect(url_for("auth.login", next=request.full_path))
elif v == "admins": elif v == ChallengeVisibilityTypes.ADMINS:
if is_admin(): if is_admin():
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
@@ -68,11 +75,11 @@ def check_challenge_visibility(f):
def check_account_visibility(f): def check_account_visibility(f):
@functools.wraps(f) @functools.wraps(f)
def _check_account_visibility(*args, **kwargs): def _check_account_visibility(*args, **kwargs):
v = get_config("account_visibility") v = get_config(ConfigTypes.ACCOUNT_VISIBILITY)
if v == "public": if v == AccountVisibilityTypes.PUBLIC:
return f(*args, **kwargs) return f(*args, **kwargs)
elif v == "private": elif v == AccountVisibilityTypes.PRIVATE:
if authed(): if authed():
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
@@ -81,7 +88,7 @@ def check_account_visibility(f):
else: else:
return redirect(url_for("auth.login", next=request.full_path)) return redirect(url_for("auth.login", next=request.full_path))
elif v == "admins": elif v == AccountVisibilityTypes.ADMINS:
if is_admin(): if is_admin():
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
@@ -93,10 +100,10 @@ def check_account_visibility(f):
def check_registration_visibility(f): def check_registration_visibility(f):
@functools.wraps(f) @functools.wraps(f)
def _check_registration_visibility(*args, **kwargs): def _check_registration_visibility(*args, **kwargs):
v = get_config("registration_visibility") v = get_config(ConfigTypes.REGISTRATION_VISIBILITY)
if v == "public": if v == RegistrationVisibilityTypes.PUBLIC:
return f(*args, **kwargs) return f(*args, **kwargs)
elif v == "private": elif v == RegistrationVisibilityTypes.PRIVATE:
abort(404) abort(404)
return _check_registration_visibility return _check_registration_visibility

View File

@@ -20,12 +20,6 @@ from CTFd.utils.config import (
is_setup, is_setup,
) )
from CTFd.utils.config.pages import get_pages 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.countries import get_countries, lookup_country_code
from CTFd.utils.dates import isoformat, unix_time, unix_time_millis from CTFd.utils.dates import isoformat, unix_time, unix_time_millis
from CTFd.utils.events import EventManager, RedisEventManager 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.plugins import Plugins
from CTFd.constants.sessions import Session from CTFd.constants.sessions import Session
from CTFd.forms import Forms 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(config=config)
app.jinja_env.globals.update(get_pages=get_pages) app.jinja_env.globals.update(get_pages=get_pages)

View File

@@ -7,6 +7,13 @@ from flask.helpers import safe_join
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from CTFd.cache import cache from CTFd.cache import cache
from CTFd.constants.config import (
AccountVisibilityTypes,
ChallengeVisibilityTypes,
ConfigTypes,
RegistrationVisibilityTypes,
ScoreVisibilityTypes,
)
from CTFd.models import ( from CTFd.models import (
Admins, Admins,
Files, Files,
@@ -156,10 +163,14 @@ def setup():
page = Pages(title=None, route="index", content=index, draft=False) page = Pages(title=None, route="index", content=index, draft=False)
# Visibility # Visibility
set_config("challenge_visibility", "private") set_config(
set_config("registration_visibility", "public") ConfigTypes.CHALLENGE_VISIBILITY, ChallengeVisibilityTypes.PRIVATE
set_config("score_visibility", "public") )
set_config("account_visibility", "public") set_config(
ConfigTypes.REGISTRATION_VISIBILITY, RegistrationVisibilityTypes.PUBLIC
)
set_config(ConfigTypes.SCORE_VISIBILITY, ScoreVisibilityTypes.PUBLIC)
set_config(ConfigTypes.ACCOUNT_VISIBILITY, AccountVisibilityTypes.PUBLIC)
# Verify emails # Verify emails
set_config("verify_emails", None) set_config("verify_emails", None)
@@ -354,7 +365,7 @@ def files(path):
# Check user is admin if challenge_visibility is admins only # Check user is admin if challenge_visibility is admins only
if ( if (
get_config("challenge_visibility") == "admins" get_config(ConfigTypes.CHALLENGE_VISIBILITY) == "admins"
and user.type != "admin" and user.type != "admin"
): ):
abort(403) abort(403)