mirror of https://github.com/JohnHammond/CTFd.git
2.3.0 (#1248)
2.3.0 / 2020-02-17 ================== **General** * During setup, admins can register their email address with the CTFd LLC newsletter for news and updates * Fix editting hints from the admin panel * Allow admins to insert HTML code directly into the header and footer (end of body tag) of pages. This replaces and supercedes the custom CSS feature. * The `views.custom_css` route has been removed. * Admins can now customize the content of outgoing emails and inject certain variables into email content. * The `manage.py` script can now manipulate the CTFd Configs table via the `get_config` and `set_config` commands. (e.g. `python manage.py get_config ctf_theme` and `python manage.py set_config ctf_theme core`) **Themes** * Themes should now reference the `theme_header` and `theme_footer` configs instead of the `views.custom_css` endpoint to allow for user customizations. See the `base.html` file of the core theme. **Plugins** * Make `ezq` functions available to `CTFd.js` under `CTFd.ui.ezq` **Miscellaneous** * Python imports sorted with `isort` and import order enforced * Black formatter running on a majority of Python codebulk-clear-sessions 2.3.0
parent
354954bbe9
commit
22c132358e
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -1,3 +1,25 @@
|
|||
2.3.0 / 2020-02-17
|
||||
==================
|
||||
|
||||
**General**
|
||||
* During setup, admins can register their email address with the CTFd LLC newsletter for news and updates
|
||||
* Fix editting hints from the admin panel
|
||||
* Allow admins to insert HTML code directly into the header and footer (end of body tag) of pages. This replaces and supercedes the custom CSS feature.
|
||||
* The `views.custom_css` route has been removed.
|
||||
* Admins can now customize the content of outgoing emails and inject certain variables into email content.
|
||||
* The `manage.py` script can now manipulate the CTFd Configs table via the `get_config` and `set_config` commands. (e.g. `python manage.py get_config ctf_theme` and `python manage.py set_config ctf_theme core`)
|
||||
|
||||
**Themes**
|
||||
* Themes should now reference the `theme_header` and `theme_footer` configs instead of the `views.custom_css` endpoint to allow for user customizations. See the `base.html` file of the core theme.
|
||||
|
||||
**Plugins**
|
||||
* Make `ezq` functions available to `CTFd.js` under `CTFd.ui.ezq`
|
||||
|
||||
**Miscellaneous**
|
||||
* Python imports sorted with `isort` and import order enforced
|
||||
* Black formatter running on a majority of Python code
|
||||
|
||||
|
||||
2.2.3 / 2020-01-21
|
||||
==================
|
||||
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
import sys
|
||||
import datetime
|
||||
import os
|
||||
|
||||
import sys
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
from flask import Flask, Request
|
||||
from flask_migrate import upgrade
|
||||
from werkzeug.utils import cached_property
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from jinja2 import FileSystemLoader
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
from six.moves import input
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from CTFd import utils
|
||||
from CTFd.utils.migrations import migrations, create_database, stamp_latest_revision
|
||||
from CTFd.utils.sessions import CachingSessionInterface
|
||||
from CTFd.utils.updates import update_check
|
||||
from CTFd.plugins import init_plugins
|
||||
from CTFd.utils.crypto import sha256
|
||||
from CTFd.utils.initialization import (
|
||||
init_events,
|
||||
init_logs,
|
||||
init_request_processors,
|
||||
init_template_filters,
|
||||
init_template_globals,
|
||||
init_logs,
|
||||
init_events,
|
||||
)
|
||||
from CTFd.utils.crypto import sha256
|
||||
from CTFd.plugins import init_plugins
|
||||
import datetime
|
||||
from CTFd.utils.migrations import create_database, migrations, stamp_latest_revision
|
||||
from CTFd.utils.sessions import CachingSessionInterface
|
||||
from CTFd.utils.updates import update_check
|
||||
|
||||
# Hack to support Unicode in Python 2 properly
|
||||
if sys.version_info[0] < 3:
|
||||
reload(sys) # noqa: F821
|
||||
sys.setdefaultencoding("utf-8")
|
||||
|
||||
__version__ = "2.2.3"
|
||||
__version__ = "2.3.0"
|
||||
|
||||
|
||||
class CTFdRequest(Request):
|
||||
|
|
|
@ -1,54 +1,51 @@
|
|||
from flask import (
|
||||
current_app as app,
|
||||
render_template,
|
||||
request,
|
||||
redirect,
|
||||
url_for,
|
||||
Blueprint,
|
||||
abort,
|
||||
render_template_string,
|
||||
send_file,
|
||||
)
|
||||
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.user import is_admin
|
||||
from CTFd.utils.security.auth import logout_user
|
||||
from CTFd.utils import config as ctf_config, get_config, set_config
|
||||
from CTFd.cache import cache, clear_config
|
||||
from CTFd.utils.helpers import get_errors
|
||||
from CTFd.utils.exports import (
|
||||
export_ctf as export_ctf_util,
|
||||
import_ctf as import_ctf_util,
|
||||
)
|
||||
from CTFd.models import (
|
||||
db,
|
||||
get_class_by_tablename,
|
||||
Users,
|
||||
Teams,
|
||||
Configs,
|
||||
Submissions,
|
||||
Solves,
|
||||
Awards,
|
||||
Unlocks,
|
||||
Tracking,
|
||||
)
|
||||
import csv
|
||||
import datetime
|
||||
import os
|
||||
import six
|
||||
import csv
|
||||
|
||||
import six
|
||||
from flask import Blueprint, abort
|
||||
from flask import current_app as app
|
||||
from flask import (
|
||||
redirect,
|
||||
render_template,
|
||||
render_template_string,
|
||||
request,
|
||||
send_file,
|
||||
url_for,
|
||||
)
|
||||
|
||||
from CTFd.cache import cache, clear_config
|
||||
from CTFd.models import (
|
||||
Awards,
|
||||
Configs,
|
||||
Solves,
|
||||
Submissions,
|
||||
Teams,
|
||||
Tracking,
|
||||
Unlocks,
|
||||
Users,
|
||||
db,
|
||||
get_class_by_tablename,
|
||||
)
|
||||
from CTFd.utils import config as ctf_config
|
||||
from CTFd.utils import get_config, set_config
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.exports import export_ctf as export_ctf_util
|
||||
from CTFd.utils.exports import import_ctf as import_ctf_util
|
||||
from CTFd.utils.helpers import get_errors
|
||||
from CTFd.utils.security.auth import logout_user
|
||||
from CTFd.utils.user import is_admin
|
||||
|
||||
admin = Blueprint("admin", __name__)
|
||||
|
||||
|
||||
from CTFd.admin import challenges # noqa: F401
|
||||
from CTFd.admin import notifications # noqa: F401
|
||||
from CTFd.admin import pages # noqa: F401
|
||||
from CTFd.admin import scoreboard # noqa: F401
|
||||
from CTFd.admin import statistics # noqa: F401
|
||||
from CTFd.admin import submissions # noqa: F401
|
||||
from CTFd.admin import teams # noqa: F401
|
||||
from CTFd.admin import users # noqa: F401
|
||||
from CTFd.admin import submissions # noqa: F401
|
||||
from CTFd.admin import notifications # noqa: F401
|
||||
|
||||
|
||||
@admin.route("/admin", methods=["GET"])
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
from flask import current_app as app, render_template, render_template_string, url_for
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils import binary_type
|
||||
from CTFd.models import Solves, Challenges, Flags
|
||||
from CTFd.plugins.challenges import get_chal_class
|
||||
from CTFd.admin import admin
|
||||
import os
|
||||
|
||||
import six
|
||||
from flask import current_app as app
|
||||
from flask import render_template, render_template_string, url_for
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Challenges, Flags, Solves
|
||||
from CTFd.plugins.challenges import get_chal_class
|
||||
from CTFd.utils import binary_type
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
||||
@admin.route("/admin/challenges")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from flask import render_template
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.models import Notifications
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Notifications
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
||||
@admin.route("/admin/notifications")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from flask import render_template, request
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Pages
|
||||
from CTFd.schemas.pages import PageSchema
|
||||
from CTFd.utils import markdown
|
||||
from CTFd.admin import admin
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
||||
@admin.route("/admin/pages")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from flask import render_template
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.scoreboard import get_standings
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
||||
@admin.route("/admin/scoreboard")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
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, Teams, Users
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Challenges, Fails, Solves, Teams, Tracking, Users, db
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.modes import get_model
|
||||
from CTFd.utils.updates import update_check
|
||||
|
||||
|
||||
@admin.route("/admin/statistics", methods=["GET"])
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from flask import render_template, request
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.models import Challenges, Submissions
|
||||
from CTFd.utils.modes import get_model
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Challenges, Submissions
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.modes import get_model
|
||||
|
||||
|
||||
@admin.route("/admin/submissions", defaults={"submission_type": None})
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from flask import render_template, request
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.models import db, Teams, Challenges, Tracking
|
||||
from CTFd.admin import admin
|
||||
from CTFd.utils.helpers import get_errors
|
||||
|
||||
from sqlalchemy.sql import not_
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Challenges, Teams, Tracking, db
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.helpers import get_errors
|
||||
|
||||
|
||||
@admin.route("/admin/teams")
|
||||
@admins_only
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from flask import render_template, request
|
||||
from sqlalchemy.sql import not_
|
||||
|
||||
from CTFd.admin import admin
|
||||
from CTFd.models import Challenges, Tracking, Users, db
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.modes import TEAMS_MODE
|
||||
from CTFd.models import db, Users, Challenges, Tracking
|
||||
from CTFd.admin import admin
|
||||
from CTFd.utils.helpers import get_errors
|
||||
|
||||
from sqlalchemy.sql import not_
|
||||
from CTFd.utils.modes import TEAMS_MODE
|
||||
|
||||
|
||||
@admin.route("/admin/users")
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
from flask import Blueprint, current_app
|
||||
from flask_restplus import Api
|
||||
from CTFd.api.v1.challenges import challenges_namespace
|
||||
|
||||
from CTFd.api.v1.teams import teams_namespace
|
||||
from CTFd.api.v1.users import users_namespace
|
||||
from CTFd.api.v1.awards import awards_namespace
|
||||
from CTFd.api.v1.challenges import challenges_namespace
|
||||
from CTFd.api.v1.config import configs_namespace
|
||||
from CTFd.api.v1.files import files_namespace
|
||||
from CTFd.api.v1.flags import flags_namespace
|
||||
from CTFd.api.v1.hints import hints_namespace
|
||||
from CTFd.api.v1.notifications import notifications_namespace
|
||||
from CTFd.api.v1.pages import pages_namespace
|
||||
from CTFd.api.v1.scoreboard import scoreboard_namespace
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from CTFd.api.v1.submissions import submissions_namespace
|
||||
from CTFd.api.v1.tags import tags_namespace
|
||||
from CTFd.api.v1.awards import awards_namespace
|
||||
from CTFd.api.v1.hints import hints_namespace
|
||||
from CTFd.api.v1.flags import flags_namespace
|
||||
from CTFd.api.v1.files import files_namespace
|
||||
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.teams import teams_namespace
|
||||
from CTFd.api.v1.tokens import tokens_namespace
|
||||
from CTFd.api.v1.unlocks import unlocks_namespace
|
||||
from CTFd.api.v1.users import users_namespace
|
||||
|
||||
api = Blueprint("api", __name__, url_prefix="/api/v1")
|
||||
CTFd_API_v1 = Api(api, version="v1", doc=current_app.config.get("SWAGGER_UI"))
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.models import db, Awards
|
||||
from CTFd.models import Awards, db
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
|
|
@ -1,48 +1,37 @@
|
|||
from flask import request, abort, url_for
|
||||
import datetime
|
||||
|
||||
from flask import abort, request, url_for
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import (
|
||||
db,
|
||||
Challenges,
|
||||
HintUnlocks,
|
||||
Tags,
|
||||
Hints,
|
||||
Flags,
|
||||
Solves,
|
||||
Fails,
|
||||
ChallengeFiles as ChallengeFilesModel,
|
||||
from sqlalchemy.sql import and_
|
||||
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.models import ChallengeFiles as ChallengeFilesModel
|
||||
from CTFd.models import Challenges, Fails, Flags, Hints, HintUnlocks, Solves, Tags, db
|
||||
from CTFd.plugins.challenges import CHALLENGE_CLASSES, get_chal_class
|
||||
from CTFd.schemas.flags import FlagSchema
|
||||
from CTFd.schemas.hints import HintSchema
|
||||
from CTFd.schemas.tags import TagSchema
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.utils import user as current_user
|
||||
from CTFd.utils.config.visibility import (
|
||||
accounts_visible,
|
||||
challenges_visible,
|
||||
scores_visible,
|
||||
)
|
||||
from CTFd.plugins.challenges import CHALLENGE_CLASSES
|
||||
from CTFd.utils.dates import isoformat
|
||||
from CTFd.utils.dates import ctf_ended, ctf_paused, ctftime, isoformat, unix_time_to_utc
|
||||
from CTFd.utils.decorators import (
|
||||
admins_only,
|
||||
during_ctf_time_only,
|
||||
require_verified_emails,
|
||||
admins_only,
|
||||
)
|
||||
from CTFd.utils.decorators.visibility import (
|
||||
check_challenge_visibility,
|
||||
check_score_visibility,
|
||||
)
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.utils.config.visibility import (
|
||||
scores_visible,
|
||||
accounts_visible,
|
||||
challenges_visible,
|
||||
)
|
||||
from CTFd.utils.user import is_admin, authed
|
||||
from CTFd.utils.modes import get_model, generate_account_url
|
||||
from CTFd.schemas.tags import TagSchema
|
||||
from CTFd.schemas.hints import HintSchema
|
||||
from CTFd.schemas.flags import FlagSchema
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.utils import user as current_user
|
||||
from CTFd.utils.user import get_current_team
|
||||
from CTFd.utils.user import get_current_user
|
||||
from CTFd.plugins.challenges import get_chal_class
|
||||
from CTFd.utils.dates import ctf_ended, ctf_paused, ctftime, unix_time_to_utc
|
||||
from CTFd.utils.logging import log
|
||||
from CTFd.utils.modes import generate_account_url, get_model
|
||||
from CTFd.utils.security.signing import serialize
|
||||
from sqlalchemy.sql import and_
|
||||
import datetime
|
||||
from CTFd.utils.user import authed, get_current_team, get_current_user, is_admin
|
||||
|
||||
challenges_namespace = Namespace(
|
||||
"challenges", description="Endpoint to retrieve Challenges"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Configs
|
||||
from CTFd.schemas.config import ConfigSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils import get_config, set_config
|
||||
|
||||
from CTFd.cache import clear_config, clear_standings
|
||||
from CTFd.models import Configs, db
|
||||
from CTFd.schemas.config import ConfigSchema
|
||||
from CTFd.utils import get_config, set_config
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
configs_namespace = Namespace("configs", description="Endpoint to retrieve Configs")
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Files
|
||||
|
||||
from CTFd.models import Files, db
|
||||
from CTFd.schemas.files import FileSchema
|
||||
from CTFd.utils import uploads
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Flags
|
||||
|
||||
from CTFd.models import Flags, db
|
||||
from CTFd.plugins.flags import FLAG_CLASSES, get_flag_class
|
||||
from CTFd.schemas.flags import FlagSchema
|
||||
from CTFd.plugins.flags import get_flag_class, FLAG_CLASSES
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
flags_namespace = Namespace("flags", description="Endpoint to retrieve Flags")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Hints, HintUnlocks
|
||||
from CTFd.utils.user import get_current_user, is_admin
|
||||
|
||||
from CTFd.models import Hints, HintUnlocks, db
|
||||
from CTFd.schemas.hints import HintSchema
|
||||
from CTFd.utils.decorators import during_ctf_time_only, admins_only, authed_only
|
||||
from CTFd.utils.decorators import admins_only, authed_only, during_ctf_time_only
|
||||
from CTFd.utils.user import get_current_user, is_admin
|
||||
|
||||
hints_namespace = Namespace("hints", description="Endpoint to retrieve Hints")
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from flask import current_app, request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Notifications
|
||||
from CTFd.schemas.notifications import NotificationSchema
|
||||
|
||||
from CTFd.models import Notifications, db
|
||||
from CTFd.schemas.notifications import NotificationSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
notifications_namespace = Namespace(
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Pages
|
||||
from CTFd.schemas.pages import PageSchema
|
||||
from CTFd.cache import clear_pages
|
||||
|
||||
from CTFd.cache import clear_pages
|
||||
from CTFd.models import Pages, db
|
||||
from CTFd.schemas.pages import PageSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
pages_namespace = Namespace("pages", description="Endpoint to retrieve Pages")
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
from flask_restplus import Namespace, Resource
|
||||
|
||||
from CTFd.models import Solves, Awards, Teams
|
||||
from CTFd.cache import cache, make_cache_key
|
||||
from CTFd.utils.scores import get_standings
|
||||
from CTFd.models import Awards, Solves, Teams
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.modes import generate_account_url, get_mode_as_word, TEAMS_MODE
|
||||
from CTFd.utils.dates import unix_time_to_utc, isoformat
|
||||
from CTFd.utils.dates import isoformat, unix_time_to_utc
|
||||
from CTFd.utils.decorators.visibility import (
|
||||
check_account_visibility,
|
||||
check_score_visibility,
|
||||
)
|
||||
from CTFd.utils.modes import TEAMS_MODE, generate_account_url, get_mode_as_word
|
||||
from CTFd.utils.scores import get_standings
|
||||
|
||||
scoreboard_namespace = Namespace(
|
||||
"scoreboard", description="Endpoint to retrieve scores"
|
||||
|
|
|
@ -5,6 +5,6 @@ statistics_namespace = Namespace(
|
|||
)
|
||||
|
||||
from CTFd.api.v1.statistics import challenges # noqa: F401
|
||||
from CTFd.api.v1.statistics import submissions # noqa: F401
|
||||
from CTFd.api.v1.statistics import teams # noqa: F401
|
||||
from CTFd.api.v1.statistics import users # noqa: F401
|
||||
from CTFd.api.v1.statistics import submissions # noqa: F401
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from flask_restplus import Resource
|
||||
from CTFd.models import db, Challenges, Solves
|
||||
from CTFd.utils.modes import get_model
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.sql import or_
|
||||
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from CTFd.models import Challenges, Solves, db
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.utils.modes import get_model
|
||||
|
||||
|
||||
@statistics_namespace.route("/challenges/<column>")
|
||||
class ChallengePropertyCounts(Resource):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from flask_restplus import Resource
|
||||
from sqlalchemy import func
|
||||
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from CTFd.models import Submissions
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from sqlalchemy import func
|
||||
|
||||
|
||||
@statistics_namespace.route("/submissions/<column>")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from flask_restplus import Resource
|
||||
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from CTFd.models import Teams
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
|
||||
|
||||
@statistics_namespace.route("/teams")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from flask_restplus import Resource
|
||||
from CTFd.models import Users
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from CTFd.utils.decorators import admins_only
|
||||
from sqlalchemy import func
|
||||
|
||||
from CTFd.api.v1.statistics import statistics_namespace
|
||||
from CTFd.models import Users
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
||||
@statistics_namespace.route("/users")
|
||||
class UserStatistics(Resource):
|
||||
|
|
|
@ -2,7 +2,7 @@ from flask import request
|
|||
from flask_restplus import Namespace, Resource
|
||||
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.models import db, Submissions
|
||||
from CTFd.models import Submissions, db
|
||||
from CTFd.schemas.submissions import SubmissionSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
from CTFd.models import db, Tags
|
||||
|
||||
from CTFd.models import Tags, db
|
||||
from CTFd.schemas.tags import TagSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
from flask import session, request, abort
|
||||
import copy
|
||||
|
||||
from flask import abort, request, session
|
||||
from flask_restplus import Namespace, Resource
|
||||
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
|
||||
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.models import Awards, Submissions, Teams, Unlocks, Users, db
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.schemas.submissions import SubmissionSchema
|
||||
from CTFd.schemas.teams import TeamSchema
|
||||
from CTFd.utils.decorators import admins_only, authed_only, require_team
|
||||
from CTFd.utils.decorators.visibility import (
|
||||
check_account_visibility,
|
||||
check_score_visibility,
|
||||
)
|
||||
from CTFd.utils.user import get_current_team, is_admin
|
||||
from CTFd.utils.decorators import authed_only, admins_only, require_team
|
||||
import copy
|
||||
|
||||
teams_namespace = Namespace("teams", description="Endpoint to retrieve Teams")
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import datetime
|
||||
|
||||
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.models import Tokens, db
|
||||
from CTFd.schemas.tokens import TokenSchema
|
||||
from CTFd.utils.decorators import authed_only, require_verified_emails
|
||||
from CTFd.utils.security.auth import generate_user_token
|
||||
from CTFd.utils.decorators import require_verified_emails, authed_only
|
||||
import datetime
|
||||
from CTFd.utils.user import get_current_user, is_admin
|
||||
|
||||
tokens_namespace = Namespace("tokens", description="Endpoint to retrieve Tokens")
|
||||
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
from flask import request
|
||||
from flask_restplus import Namespace, Resource
|
||||
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.models import db, get_class_by_tablename, Unlocks
|
||||
from CTFd.utils.user import get_current_user
|
||||
from CTFd.schemas.unlocks import UnlockSchema
|
||||
from CTFd.models import Unlocks, db, get_class_by_tablename
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.schemas.unlocks import UnlockSchema
|
||||
from CTFd.utils.decorators import (
|
||||
during_ctf_time_only,
|
||||
require_verified_emails,
|
||||
admins_only,
|
||||
authed_only,
|
||||
during_ctf_time_only,
|
||||
require_verified_emails,
|
||||
)
|
||||
from CTFd.utils.user import get_current_user
|
||||
|
||||
unlocks_namespace = Namespace("unlocks", description="Endpoint to retrieve Unlocks")
|
||||
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
from flask import session, request, abort
|
||||
from flask import abort, request, session
|
||||
from flask_restplus import Namespace, Resource
|
||||
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.models import (
|
||||
db,
|
||||
Users,
|
||||
Solves,
|
||||
Awards,
|
||||
Notifications,
|
||||
Solves,
|
||||
Submissions,
|
||||
Tracking,
|
||||
Unlocks,
|
||||
Submissions,
|
||||
Notifications,
|
||||
Users,
|
||||
db,
|
||||
)
|
||||
from CTFd.utils.decorators import authed_only, admins_only, ratelimit
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.schemas.submissions import SubmissionSchema
|
||||
from CTFd.schemas.users import UserSchema
|
||||
from CTFd.utils.config import get_mail_provider
|
||||
from CTFd.utils.email import sendmail, user_created_notification
|
||||
from CTFd.utils.user import get_current_user, is_admin
|
||||
from CTFd.utils.decorators import admins_only, authed_only, ratelimit
|
||||
from CTFd.utils.decorators.visibility import (
|
||||
check_account_visibility,
|
||||
check_score_visibility,
|
||||
)
|
||||
|
||||
from CTFd.schemas.submissions import SubmissionSchema
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.schemas.users import UserSchema
|
||||
|
||||
from CTFd.utils.email import sendmail, user_created_notification
|
||||
from CTFd.utils.user import get_current_user, is_admin
|
||||
|
||||
users_namespace = Namespace("users", description="Endpoint to retrieve Users")
|
||||
|
||||
|
|
48
CTFd/auth.py
48
CTFd/auth.py
|
@ -1,33 +1,25 @@
|
|||
from flask import (
|
||||
current_app as app,
|
||||
render_template,
|
||||
request,
|
||||
redirect,
|
||||
url_for,
|
||||
session,
|
||||
Blueprint,
|
||||
)
|
||||
from itsdangerous.exc import BadTimeSignature, SignatureExpired, BadSignature
|
||||
import base64
|
||||
|
||||
from CTFd.models import db, Users, Teams
|
||||
import requests
|
||||
from flask import Blueprint
|
||||
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.utils import get_config, get_app_config
|
||||
from CTFd.utils.decorators import ratelimit
|
||||
from CTFd.models import Teams, Users, db
|
||||
from CTFd.utils import config, email, get_app_config, get_config
|
||||
from CTFd.utils import user as current_user
|
||||
from CTFd.utils import config, validators
|
||||
from CTFd.utils import email
|
||||
from CTFd.utils.security.auth import login_user, logout_user
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils.logging import log
|
||||
from CTFd.utils.decorators.visibility import check_registration_visibility
|
||||
from CTFd.utils import validators
|
||||
from CTFd.utils.config import is_teams_mode
|
||||
from CTFd.utils.config.visibility import registration_visible
|
||||
from CTFd.utils.modes import TEAMS_MODE
|
||||
from CTFd.utils.security.signing import unserialize
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils.decorators import ratelimit
|
||||
from CTFd.utils.decorators.visibility import check_registration_visibility
|
||||
from CTFd.utils.helpers import error_for, get_errors
|
||||
|
||||
import base64
|
||||
import requests
|
||||
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
|
||||
|
||||
auth = Blueprint("auth", __name__)
|
||||
|
||||
|
@ -61,6 +53,7 @@ def confirm(data=None):
|
|||
name=user.name,
|
||||
)
|
||||
db.session.commit()
|
||||
email.successful_registration_notification(user.email)
|
||||
db.session.close()
|
||||
if current_user.authed():
|
||||
return redirect(url_for("challenges.listing"))
|
||||
|
@ -251,12 +244,7 @@ def register():
|
|||
if (
|
||||
config.can_send_mail()
|
||||
): # We want to notify the user that they have registered.
|
||||
email.sendmail(
|
||||
request.form["email"],
|
||||
"You've successfully registered for {}".format(
|
||||
get_config("ctf_name")
|
||||
),
|
||||
)
|
||||
email.successful_registration_notification(user.email)
|
||||
|
||||
log("registrations", "[{date}] {ip} - {name} registered with {email}")
|
||||
db.session.close()
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from flask import render_template, Blueprint
|
||||
from CTFd.utils.decorators import (
|
||||
during_ctf_time_only,
|
||||
require_verified_emails,
|
||||
require_team,
|
||||
)
|
||||
from CTFd.utils.decorators.visibility import check_challenge_visibility
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.utils.dates import ctf_ended, ctf_paused, view_after_ctf
|
||||
from CTFd.utils.decorators import (
|
||||
during_ctf_time_only,
|
||||
require_team,
|
||||
require_verified_emails,
|
||||
)
|
||||
from CTFd.utils.decorators.visibility import check_challenge_visibility
|
||||
from CTFd.utils.helpers import get_errors, get_infos
|
||||
|
||||
challenges = Blueprint("challenges", __name__)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from flask import current_app, Blueprint, Response, stream_with_context
|
||||
from flask import Blueprint, Response, current_app, stream_with_context
|
||||
|
||||
from CTFd.utils import get_app_config
|
||||
from CTFd.utils.decorators import authed_only, ratelimit
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
from flask_sqlalchemy import SQLAlchemy
|
||||
import datetime
|
||||
|
||||
import six
|
||||
from flask_marshmallow import Marshmallow
|
||||
from sqlalchemy.orm import validates, column_property
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.orm import column_property, validates
|
||||
|
||||
from CTFd.cache import cache
|
||||
from CTFd.utils.crypto import hash_password
|
||||
from CTFd.utils.humanize.numbers import ordinalize
|
||||
from CTFd.cache import cache
|
||||
import datetime
|
||||
import six
|
||||
|
||||
db = SQLAlchemy()
|
||||
ma = Marshmallow()
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
import glob
|
||||
import importlib
|
||||
import os
|
||||
|
||||
from collections import namedtuple
|
||||
from flask import current_app as app, send_file, send_from_directory
|
||||
|
||||
from flask import current_app as app
|
||||
from flask import send_file, send_from_directory
|
||||
|
||||
from CTFd.utils.config.pages import get_pages
|
||||
from CTFd.utils.decorators import admins_only as admins_only_wrapper
|
||||
from CTFd.utils.plugins import override_template as utils_override_template
|
||||
from CTFd.utils.plugins import (
|
||||
override_template as utils_override_template,
|
||||
register_script as utils_register_plugin_script,
|
||||
register_stylesheet as utils_register_plugin_stylesheet,
|
||||
register_admin_script as utils_register_admin_plugin_script,
|
||||
)
|
||||
from CTFd.utils.plugins import (
|
||||
register_admin_stylesheet as utils_register_admin_plugin_stylesheet,
|
||||
)
|
||||
from CTFd.utils.config.pages import get_pages
|
||||
|
||||
from CTFd.utils.plugins import register_script as utils_register_plugin_script
|
||||
from CTFd.utils.plugins import register_stylesheet as utils_register_plugin_stylesheet
|
||||
|
||||
Menu = namedtuple("Menu", ["title", "route"])
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
from CTFd.plugins import register_plugin_assets_directory
|
||||
from CTFd.plugins.flags import get_flag_class
|
||||
from flask import Blueprint
|
||||
|
||||
from CTFd.models import (
|
||||
db,
|
||||
Solves,
|
||||
ChallengeFiles,
|
||||
Challenges,
|
||||
Fails,
|
||||
Flags,
|
||||
Challenges,
|
||||
ChallengeFiles,
|
||||
Tags,
|
||||
Hints,
|
||||
Solves,
|
||||
Tags,
|
||||
db,
|
||||
)
|
||||
from CTFd.utils.user import get_ip
|
||||
from CTFd.plugins import register_plugin_assets_directory
|
||||
from CTFd.plugins.flags import get_flag_class
|
||||
from CTFd.utils.uploads import delete_file
|
||||
from flask import Blueprint
|
||||
from CTFd.utils.user import get_ip
|
||||
|
||||
|
||||
class BaseChallenge(object):
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
from __future__ import division # Use floating point for math calculations
|
||||
from CTFd.plugins.challenges import BaseChallenge, CHALLENGE_CLASSES
|
||||
from CTFd.plugins import register_plugin_assets_directory
|
||||
from CTFd.plugins.flags import get_flag_class
|
||||
|
||||
import math
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
from CTFd.models import (
|
||||
db,
|
||||
Solves,
|
||||
ChallengeFiles,
|
||||
Challenges,
|
||||
Fails,
|
||||
Flags,
|
||||
Challenges,
|
||||
ChallengeFiles,
|
||||
Tags,
|
||||
Hints,
|
||||
Solves,
|
||||
Tags,
|
||||
db,
|
||||
)
|
||||
from CTFd.utils.user import get_ip
|
||||
from CTFd.utils.uploads import delete_file
|
||||
from CTFd.plugins import register_plugin_assets_directory
|
||||
from CTFd.plugins.challenges import CHALLENGE_CLASSES, BaseChallenge
|
||||
from CTFd.plugins.flags import get_flag_class
|
||||
from CTFd.utils.modes import get_model
|
||||
from flask import Blueprint
|
||||
import math
|
||||
from CTFd.utils.uploads import delete_file
|
||||
from CTFd.utils.user import get_ip
|
||||
|
||||
|
||||
class DynamicValueChallenge(BaseChallenge):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from CTFd.plugins import register_plugin_assets_directory
|
||||
|
||||
import re
|
||||
|
||||
from CTFd.plugins import register_plugin_assets_directory
|
||||
|
||||
|
||||
class BaseFlag(object):
|
||||
name = None
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Awards
|
||||
from CTFd.models import Awards, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Challenges
|
||||
from CTFd.models import Challenges, ma
|
||||
|
||||
|
||||
class ChallengeSchema(ma.ModelSchema):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Configs
|
||||
from CTFd.models import Configs, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Files
|
||||
from CTFd.models import Files, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Flags
|
||||
from CTFd.models import Flags, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Hints
|
||||
from CTFd.models import Hints, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Notifications
|
||||
from CTFd.models import Notifications, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from marshmallow import pre_load
|
||||
from CTFd.models import ma, Pages
|
||||
|
||||
from CTFd.models import Pages, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from marshmallow import fields
|
||||
|
||||
from CTFd.models import Submissions, ma
|
||||
from CTFd.schemas.challenges import ChallengeSchema
|
||||
from CTFd.models import ma, Submissions
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Tags
|
||||
from CTFd.models import Tags, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from marshmallow import validate, ValidationError, pre_load
|
||||
from marshmallow import ValidationError, pre_load, validate
|
||||
from marshmallow_sqlalchemy import field_for
|
||||
from CTFd.models import ma, Teams, Users
|
||||
from CTFd.utils.validators import validate_country_code
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.user import is_admin, get_current_team, get_current_user
|
||||
|
||||
from CTFd.models import Teams, Users, ma
|
||||
from CTFd.utils import get_config, string_types
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils import string_types
|
||||
from CTFd.utils.user import get_current_team, get_current_user, is_admin
|
||||
from CTFd.utils.validators import validate_country_code
|
||||
|
||||
|
||||
class TeamSchema(ma.ModelSchema):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Tokens
|
||||
from CTFd.models import Tokens, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from CTFd.models import ma, Unlocks
|
||||
from CTFd.models import Unlocks, ma
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from marshmallow import validate, ValidationError, pre_load
|
||||
from marshmallow import ValidationError, pre_load, validate
|
||||
from marshmallow_sqlalchemy import field_for
|
||||
from CTFd.models import ma, Users
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.validators import validate_country_code
|
||||
from CTFd.utils.user import is_admin, get_current_user
|
||||
|
||||
from CTFd.models import Users, ma
|
||||
from CTFd.utils import get_config, string_types
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils.email import check_email_is_whitelisted
|
||||
from CTFd.utils import string_types
|
||||
from CTFd.utils.user import get_current_user, is_admin
|
||||
from CTFd.utils.validators import validate_country_code
|
||||
|
||||
|
||||
class UserSchema(ma.ModelSchema):
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
from flask import render_template, Blueprint
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
from CTFd.cache import cache, make_cache_key
|
||||
from CTFd.utils import config
|
||||
from CTFd.utils.decorators.visibility import check_score_visibility
|
||||
|
||||
from CTFd.utils.scores import get_standings
|
||||
|
||||
scoreboard = Blueprint("scoreboard", __name__)
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
from flask import render_template, request, redirect, url_for, Blueprint
|
||||
from CTFd.models import db, Teams
|
||||
from flask import Blueprint, redirect, render_template, request, url_for
|
||||
|
||||
from CTFd.models import Teams, db
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils.decorators import authed_only, ratelimit
|
||||
from CTFd.utils.decorators.modes import require_team_mode
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.utils.user import get_current_user
|
||||
from CTFd.utils.crypto import verify_password
|
||||
from CTFd.utils.decorators.visibility import (
|
||||
check_account_visibility,
|
||||
check_score_visibility,
|
||||
)
|
||||
from CTFd.utils.helpers import get_errors, get_infos
|
||||
from CTFd.utils.user import get_current_user
|
||||
|
||||
teams = Blueprint("teams", __name__)
|
||||
|
||||
|
@ -72,7 +73,9 @@ def join():
|
|||
name=team.name, limit=team_size_limit
|
||||
)
|
||||
)
|
||||
return render_template("teams/join_team.html", infos=infos, errors=errors)
|
||||
return render_template(
|
||||
"teams/join_team.html", infos=infos, errors=errors
|
||||
)
|
||||
|
||||
user.team_id = team.id
|
||||
db.session.commit()
|
||||
|
|
|
@ -118,8 +118,8 @@ export function editHint(event) {
|
|||
const params = $(this).serializeJSON(true);
|
||||
params["challenge"] = CHALLENGE_ID;
|
||||
|
||||
const method = "POST";
|
||||
const url = "/api/v1/hints";
|
||||
let method = "POST";
|
||||
let url = "/api/v1/hints";
|
||||
if (params.id) {
|
||||
method = "PATCH";
|
||||
url = "/api/v1/hints/" + params.id;
|
||||
|
|
|
@ -32,7 +32,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
|
|||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _fetch = _interopRequireDefault(__webpack_require__(/*! ./fetch */ \"./CTFd/themes/core/assets/js/fetch.js\"));\n\nvar _config = _interopRequireDefault(__webpack_require__(/*! ./config */ \"./CTFd/themes/core/assets/js/config.js\"));\n\nvar _api = __webpack_require__(/*! ./api */ \"./CTFd/themes/core/assets/js/api.js\");\n\n__webpack_require__(/*! ./patch */ \"./CTFd/themes/core/assets/js/patch.js\");\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { if (i % 2) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } else { Object.defineProperties(target, Object.getOwnPropertyDescriptors(arguments[i])); } } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar api = new _api.API(\"/\");\nvar user = {};\nvar _internal = {};\nvar lib = {\n $: _jquery.default,\n markdown: markdown\n};\nvar initialized = false;\n\nvar init = function init(data) {\n if (initialized) {\n return;\n }\n\n initialized = true;\n _config.default.urlRoot = data.urlRoot || _config.default.urlRoot;\n _config.default.csrfNonce = data.csrfNonce || _config.default.csrfNonce;\n _config.default.userMode = data.userMode || _config.default.userMode;\n api.domain = _config.default.urlRoot + \"/api/v1\";\n user.id = data.userId;\n};\n\nvar plugin = {\n run: function run(f) {\n f(CTFd);\n }\n};\n\nfunction markdown(config) {\n // Merge passed config with original. Default to original.\n var md_config = _objectSpread({}, {\n html: true,\n linkify: true\n }, {}, config);\n\n var md = (0, _markdownIt.default)(md_config);\n\n md.renderer.rules.link_open = function (tokens, idx, options, env, self) {\n tokens[idx].attrPush([\"target\", \"_blank\"]);\n return self.renderToken(tokens, idx, options);\n };\n\n return md;\n}\n\nvar CTFd = {\n init: init,\n config: _config.default,\n fetch: _fetch.default,\n user: user,\n api: api,\n lib: lib,\n _internal: _internal,\n plugin: plugin\n};\nvar _default = CTFd;\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/CTFd.js?");
|
||||
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _fetch = _interopRequireDefault(__webpack_require__(/*! ./fetch */ \"./CTFd/themes/core/assets/js/fetch.js\"));\n\nvar _config = _interopRequireDefault(__webpack_require__(/*! ./config */ \"./CTFd/themes/core/assets/js/config.js\"));\n\nvar _api = __webpack_require__(/*! ./api */ \"./CTFd/themes/core/assets/js/api.js\");\n\n__webpack_require__(/*! ./patch */ \"./CTFd/themes/core/assets/js/patch.js\");\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _ezq = _interopRequireDefault(__webpack_require__(/*! ./ezq */ \"./CTFd/themes/core/assets/js/ezq.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { if (i % 2) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } else { Object.defineProperties(target, Object.getOwnPropertyDescriptors(arguments[i])); } } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar api = new _api.API(\"/\");\nvar user = {};\nvar _internal = {};\nvar ui = {\n ezq: _ezq.default\n};\nvar lib = {\n $: _jquery.default,\n markdown: markdown\n};\nvar initialized = false;\n\nvar init = function init(data) {\n if (initialized) {\n return;\n }\n\n initialized = true;\n _config.default.urlRoot = data.urlRoot || _config.default.urlRoot;\n _config.default.csrfNonce = data.csrfNonce || _config.default.csrfNonce;\n _config.default.userMode = data.userMode || _config.default.userMode;\n api.domain = _config.default.urlRoot + \"/api/v1\";\n user.id = data.userId;\n};\n\nvar plugin = {\n run: function run(f) {\n f(CTFd);\n }\n};\n\nfunction markdown(config) {\n // Merge passed config with original. Default to original.\n var md_config = _objectSpread({}, {\n html: true,\n linkify: true\n }, {}, config);\n\n var md = (0, _markdownIt.default)(md_config);\n\n md.renderer.rules.link_open = function (tokens, idx, options, env, self) {\n tokens[idx].attrPush([\"target\", \"_blank\"]);\n return self.renderToken(tokens, idx, options);\n };\n\n return md;\n}\n\nvar CTFd = {\n init: init,\n config: _config.default,\n fetch: _fetch.default,\n user: user,\n ui: ui,\n api: api,\n lib: lib,\n _internal: _internal,\n plugin: plugin\n};\nvar _default = CTFd;\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/CTFd.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
|
|||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.showHintModal = showHintModal;\nexports.showEditHintModal = showEditHintModal;\nexports.deleteHint = deleteHint;\nexports.editHint = editHint;\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _readOnlyError(name) { throw new Error(\"\\\"\" + name + \"\\\" is read-only\"); }\n\nfunction hint(id) {\n return _CTFd.default.fetch(\"/api/v1/hints/\" + id + \"?preview=true\", {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n });\n}\n\nfunction loadhint(hintid) {\n var md = _CTFd.default.lib.markdown();\n\n hint(hintid).then(function (response) {\n if (response.data.content) {\n (0, _ezq.ezAlert)({\n title: \"Hint\",\n body: md.render(response.data.content),\n button: \"Got it!\"\n });\n } else {\n (0, _ezq.ezAlert)({\n title: \"Error\",\n body: \"Error loading hint!\",\n button: \"OK\"\n });\n }\n });\n}\n\nfunction showHintModal(event) {\n event.preventDefault();\n (0, _jquery.default)(\"#hint-edit-modal form\").find(\"input, textarea\").val(\"\"); // Markdown Preview\n\n (0, _jquery.default)(\"#new-hint-edit\").on(\"shown.bs.tab\", function (event) {\n if (event.target.hash == \"#hint-preview\") {\n var renderer = _CTFd.default.lib.markdown();\n\n var editor_value = (0, _jquery.default)(\"#hint-write textarea\").val();\n (0, _jquery.default)(event.target.hash).html(renderer.render(editor_value));\n }\n });\n (0, _jquery.default)(\"#hint-edit-modal\").modal();\n}\n\nfunction showEditHintModal(event) {\n event.preventDefault();\n var hint_id = (0, _jquery.default)(this).attr(\"hint-id\");\n\n _CTFd.default.fetch(\"/api/v1/hints/\" + hint_id + \"?preview=true\", {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n (0, _jquery.default)(\"#hint-edit-form input[name=content],textarea[name=content]\").val(response.data.content);\n (0, _jquery.default)(\"#hint-edit-form input[name=cost]\").val(response.data.cost);\n (0, _jquery.default)(\"#hint-edit-form input[name=id]\").val(response.data.id); // Markdown Preview\n\n (0, _jquery.default)(\"#new-hint-edit\").on(\"shown.bs.tab\", function (event) {\n if (event.target.hash == \"#hint-preview\") {\n var renderer = _CTFd.default.lib.markdown();\n\n var editor_value = (0, _jquery.default)(\"#hint-write textarea\").val();\n (0, _jquery.default)(event.target.hash).html(renderer.render(editor_value));\n }\n });\n (0, _jquery.default)(\"#hint-edit-modal\").modal();\n }\n });\n}\n\nfunction deleteHint(event) {\n event.preventDefault();\n var hint_id = (0, _jquery.default)(this).attr(\"hint-id\");\n var row = (0, _jquery.default)(this).parent().parent();\n (0, _ezq.ezQuery)({\n title: \"Delete Hint\",\n body: \"Are you sure you want to delete this hint?\",\n success: function success() {\n _CTFd.default.fetch(\"/api/v1/hints/\" + hint_id, {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n row.remove();\n }\n });\n }\n });\n}\n\nfunction editHint(event) {\n event.preventDefault();\n var params = (0, _jquery.default)(this).serializeJSON(true);\n params[\"challenge\"] = CHALLENGE_ID;\n var method = \"POST\";\n var url = \"/api/v1/hints\";\n\n if (params.id) {\n method = (_readOnlyError(\"method\"), \"PATCH\");\n url = (_readOnlyError(\"url\"), \"/api/v1/hints/\" + params.id);\n }\n\n _CTFd.default.fetch(url, {\n method: method,\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n // TODO: Refresh hints on submit.\n window.location.reload();\n }\n });\n}\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/challenges/hints.js?");
|
||||
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.showHintModal = showHintModal;\nexports.showEditHintModal = showEditHintModal;\nexports.deleteHint = deleteHint;\nexports.editHint = editHint;\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction hint(id) {\n return _CTFd.default.fetch(\"/api/v1/hints/\" + id + \"?preview=true\", {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n });\n}\n\nfunction loadhint(hintid) {\n var md = _CTFd.default.lib.markdown();\n\n hint(hintid).then(function (response) {\n if (response.data.content) {\n (0, _ezq.ezAlert)({\n title: \"Hint\",\n body: md.render(response.data.content),\n button: \"Got it!\"\n });\n } else {\n (0, _ezq.ezAlert)({\n title: \"Error\",\n body: \"Error loading hint!\",\n button: \"OK\"\n });\n }\n });\n}\n\nfunction showHintModal(event) {\n event.preventDefault();\n (0, _jquery.default)(\"#hint-edit-modal form\").find(\"input, textarea\").val(\"\"); // Markdown Preview\n\n (0, _jquery.default)(\"#new-hint-edit\").on(\"shown.bs.tab\", function (event) {\n if (event.target.hash == \"#hint-preview\") {\n var renderer = _CTFd.default.lib.markdown();\n\n var editor_value = (0, _jquery.default)(\"#hint-write textarea\").val();\n (0, _jquery.default)(event.target.hash).html(renderer.render(editor_value));\n }\n });\n (0, _jquery.default)(\"#hint-edit-modal\").modal();\n}\n\nfunction showEditHintModal(event) {\n event.preventDefault();\n var hint_id = (0, _jquery.default)(this).attr(\"hint-id\");\n\n _CTFd.default.fetch(\"/api/v1/hints/\" + hint_id + \"?preview=true\", {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n }\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (response.success) {\n (0, _jquery.default)(\"#hint-edit-form input[name=content],textarea[name=content]\").val(response.data.content);\n (0, _jquery.default)(\"#hint-edit-form input[name=cost]\").val(response.data.cost);\n (0, _jquery.default)(\"#hint-edit-form input[name=id]\").val(response.data.id); // Markdown Preview\n\n (0, _jquery.default)(\"#new-hint-edit\").on(\"shown.bs.tab\", function (event) {\n if (event.target.hash == \"#hint-preview\") {\n var renderer = _CTFd.default.lib.markdown();\n\n var editor_value = (0, _jquery.default)(\"#hint-write textarea\").val();\n (0, _jquery.default)(event.target.hash).html(renderer.render(editor_value));\n }\n });\n (0, _jquery.default)(\"#hint-edit-modal\").modal();\n }\n });\n}\n\nfunction deleteHint(event) {\n event.preventDefault();\n var hint_id = (0, _jquery.default)(this).attr(\"hint-id\");\n var row = (0, _jquery.default)(this).parent().parent();\n (0, _ezq.ezQuery)({\n title: \"Delete Hint\",\n body: \"Are you sure you want to delete this hint?\",\n success: function success() {\n _CTFd.default.fetch(\"/api/v1/hints/\" + hint_id, {\n method: \"DELETE\"\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n row.remove();\n }\n });\n }\n });\n}\n\nfunction editHint(event) {\n event.preventDefault();\n var params = (0, _jquery.default)(this).serializeJSON(true);\n params[\"challenge\"] = CHALLENGE_ID;\n var method = \"POST\";\n var url = \"/api/v1/hints\";\n\n if (params.id) {\n method = \"PATCH\";\n url = \"/api/v1/hints/\" + params.id;\n }\n\n _CTFd.default.fetch(url, {\n method: method,\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (data) {\n if (data.success) {\n // TODO: Refresh hints on submit.\n window.location.reload();\n }\n });\n}\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/challenges/hints.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -106,12 +106,24 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Stylesheet editor
|
||||
Theme Header
|
||||
<small class="form-text text-muted">
|
||||
CSS is applied to all public facing pages if the theme used supports custom CSS.
|
||||
Theme headers are inserted just before the <code></head></code> tag on all public facing pages.
|
||||
Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" id="css-editor" name="css" rows="7">{{ css }}</textarea>
|
||||
<textarea class="form-control" id="theme-header" name="theme_header" rows="7">{{ theme_header or "" }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Theme Footer
|
||||
<small class="form-text text-muted">
|
||||
Theme footers are inserted just before the <code></body></code> tag on all public facing pages.
|
||||
Requires theme support.
|
||||
</small>
|
||||
</label>
|
||||
<textarea class="form-control" id="theme-footer" name="theme_footer" rows="7">{{ theme_footer or "" }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-md btn-primary float-right">Update</button>
|
||||
|
|
|
@ -1,6 +1,140 @@
|
|||
<div role="tabpanel" class="tab-pane config-section" id="email">
|
||||
<form method="POST" autocomplete="off" class="w-100">
|
||||
<ul class="nav nav-tabs mb-3" role="tablist">
|
||||
<h5>Email Content</h5>
|
||||
<small class="form-text text-muted">
|
||||
Customize CTFd emails with <a href="https://help.ctfd.io/configuration/emails/#email-content" target="_blank">predefined variables</a> and custom content
|
||||
</small>
|
||||
<ul class="nav nav-tabs mt-3" role="tablist">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link active" href="#confirmation-email-tab" role="tab" data-toggle="tab">
|
||||
Confirmation
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#account-details-email-tab" role="tab" data-toggle="tab">Account Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#password-reset-email-tab" role="tab" data-toggle="tab">Password Reset</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="confirmation-email-tab">
|
||||
<div class="form-group">
|
||||
<label class="pt-3">
|
||||
Account Registration<br>
|
||||
<small class="form-text text-muted">
|
||||
Email sent to users after they've registered their account entirely
|
||||
</small>
|
||||
</label>
|
||||
<div>
|
||||
<label>
|
||||
Subject
|
||||
</label>
|
||||
<input class="form-control" id='successful_registration_email_subject' name='successful_registration_email_subject' type='text'
|
||||
value="{{ successful_registration_email_subject or ''}}">
|
||||
<label>
|
||||
Body
|
||||
</label>
|
||||
<textarea class="form-control" type="text" id="successful_registration_email_body" name="verification_email_body"
|
||||
rows="5">{{ successful_registration_email_body or '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="pt-3">
|
||||
Account Confirmation<br>
|
||||
<small class="form-text text-muted">
|
||||
Email sent to users to confirm their account
|
||||
</small>
|
||||
</label>
|
||||
<div>
|
||||
<label>
|
||||
Subject
|
||||
</label>
|
||||
<input class="form-control" id='verification_email_subject' name='verification_email_subject' type='text' value="{{ verification_email_subject or ''}}">
|
||||
<label>
|
||||
Body
|
||||
</label>
|
||||
<textarea class="form-control" type="text" id="verification_email_body" name="verification_email_body"
|
||||
rows="5">{{ verification_email_body or '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="account-details-email-tab">
|
||||
<div class="form-group">
|
||||
<label class="pt-3">
|
||||
New Account Details<br>
|
||||
<small class="form-text text-muted">
|
||||
Email sent to new users (created by an admin) with their initial account details
|
||||
</small>
|
||||
</label>
|
||||
<div>
|
||||
<label>
|
||||
Subject
|
||||
</label>
|
||||
<input class="form-control" id='user_creation_email_subject' name='user_creation_email_subject' type='text'
|
||||
value="{{ user_creation_email_subject or ''}}">
|
||||
<label>
|
||||
Body
|
||||
</label>
|
||||
<textarea class="form-control" type="text" id="user_creation_email_body" name="user_creation_email_body"
|
||||
rows="5">{{ user_creation_email_body or '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="password-reset-email-tab">
|
||||
<div class="form-group">
|
||||
<label class="pt-3">
|
||||
Password Reset Request<br>
|
||||
<small class="form-text text-muted">
|
||||
Email sent whent a user requests a password reset
|
||||
</small>
|
||||
</label>
|
||||
<div>
|
||||
<label>
|
||||
Subject
|
||||
</label>
|
||||
<input class="form-control" id='password_reset_subject' name='password_reset_subject' type='text'
|
||||
value="{{ password_reset_subject or ''}}">
|
||||
<label>
|
||||
Body
|
||||
</label>
|
||||
<textarea class="form-control" type="text" id="password_reset_body" name="password_reset_body"
|
||||
rows="5">{{ password_reset_body or '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="pt-3">
|
||||
Password Reset Confirmation<br>
|
||||
<small class="form-text text-muted">
|
||||
Email sent whent a user successfully resets their password
|
||||
</small>
|
||||
</label>
|
||||
<div>
|
||||
<label>
|
||||
Subject
|
||||
</label>
|
||||
<input class="form-control" id='password_change_alert_subject' name='password_change_alert_subject' type='text'
|
||||
value="{{ password_change_alert_subject or ''}}">
|
||||
<label>
|
||||
Body
|
||||
</label>
|
||||
<textarea class="form-control" type="text" id="password_change_alert_body" name="password_change_alert_body"
|
||||
rows="5">{{ password_change_alert_body or '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-5">
|
||||
|
||||
<h5>Email Server</h5>
|
||||
<small class="form-text text-muted">
|
||||
Change the email server used by CTFd to send email
|
||||
</small>
|
||||
<ul class="nav nav-tabs my-3" role="tablist">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link active" href="#mailserver" role="tab" data-toggle="tab">Mail
|
||||
Server</a>
|
||||
|
|
|
@ -4,10 +4,14 @@ import { API } from "./api";
|
|||
import "./patch";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import $ from "jquery";
|
||||
import ezq from "./ezq";
|
||||
|
||||
const api = new API("/");
|
||||
const user = {};
|
||||
const _internal = {};
|
||||
const ui = {
|
||||
ezq
|
||||
};
|
||||
const lib = {
|
||||
$,
|
||||
markdown
|
||||
|
@ -47,6 +51,7 @@ const CTFd = {
|
|||
config,
|
||||
fetch,
|
||||
user,
|
||||
ui,
|
||||
api,
|
||||
lib,
|
||||
_internal,
|
||||
|
|
|
@ -109,4 +109,25 @@ $(() => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#setup-form").submit(function(e) {
|
||||
if ($("#newsletter-checkbox").prop("checked")) {
|
||||
let email = $(e.target)
|
||||
.find("input[name=email]")
|
||||
.val();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url:
|
||||
"https://ctfd.us15.list-manage.com/subscribe/post-json?u=6c7fa6feeced52775aec9d015&id=dd1484208e&c=?",
|
||||
data: {
|
||||
EMAIL: email,
|
||||
subscribe: "Subscribe",
|
||||
b_6c7fa6feeced52775aec9d015_dd1484208e: ""
|
||||
},
|
||||
dataType: "jsonp",
|
||||
contentType: "application/json; charset=utf-8"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _fetch = _interopRequireDefault(__webpack_require__(/*! ./fetch */ \"./CTFd/themes/core/assets/js/fetch.js\"));\n\nvar _config = _interopRequireDefault(__webpack_require__(/*! ./config */ \"./CTFd/themes/core/assets/js/config.js\"));\n\nvar _api = __webpack_require__(/*! ./api */ \"./CTFd/themes/core/assets/js/api.js\");\n\n__webpack_require__(/*! ./patch */ \"./CTFd/themes/core/assets/js/patch.js\");\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { if (i % 2) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } else { Object.defineProperties(target, Object.getOwnPropertyDescriptors(arguments[i])); } } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar api = new _api.API(\"/\");\nvar user = {};\nvar _internal = {};\nvar lib = {\n $: _jquery.default,\n markdown: markdown\n};\nvar initialized = false;\n\nvar init = function init(data) {\n if (initialized) {\n return;\n }\n\n initialized = true;\n _config.default.urlRoot = data.urlRoot || _config.default.urlRoot;\n _config.default.csrfNonce = data.csrfNonce || _config.default.csrfNonce;\n _config.default.userMode = data.userMode || _config.default.userMode;\n api.domain = _config.default.urlRoot + \"/api/v1\";\n user.id = data.userId;\n};\n\nvar plugin = {\n run: function run(f) {\n f(CTFd);\n }\n};\n\nfunction markdown(config) {\n // Merge passed config with original. Default to original.\n var md_config = _objectSpread({}, {\n html: true,\n linkify: true\n }, {}, config);\n\n var md = (0, _markdownIt.default)(md_config);\n\n md.renderer.rules.link_open = function (tokens, idx, options, env, self) {\n tokens[idx].attrPush([\"target\", \"_blank\"]);\n return self.renderToken(tokens, idx, options);\n };\n\n return md;\n}\n\nvar CTFd = {\n init: init,\n config: _config.default,\n fetch: _fetch.default,\n user: user,\n api: api,\n lib: lib,\n _internal: _internal,\n plugin: plugin\n};\nvar _default = CTFd;\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/CTFd.js?");
|
||||
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _fetch = _interopRequireDefault(__webpack_require__(/*! ./fetch */ \"./CTFd/themes/core/assets/js/fetch.js\"));\n\nvar _config = _interopRequireDefault(__webpack_require__(/*! ./config */ \"./CTFd/themes/core/assets/js/config.js\"));\n\nvar _api = __webpack_require__(/*! ./api */ \"./CTFd/themes/core/assets/js/api.js\");\n\n__webpack_require__(/*! ./patch */ \"./CTFd/themes/core/assets/js/patch.js\");\n\nvar _markdownIt = _interopRequireDefault(__webpack_require__(/*! markdown-it */ \"./node_modules/markdown-it/index.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _ezq = _interopRequireDefault(__webpack_require__(/*! ./ezq */ \"./CTFd/themes/core/assets/js/ezq.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { if (i % 2) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } else { Object.defineProperties(target, Object.getOwnPropertyDescriptors(arguments[i])); } } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar api = new _api.API(\"/\");\nvar user = {};\nvar _internal = {};\nvar ui = {\n ezq: _ezq.default\n};\nvar lib = {\n $: _jquery.default,\n markdown: markdown\n};\nvar initialized = false;\n\nvar init = function init(data) {\n if (initialized) {\n return;\n }\n\n initialized = true;\n _config.default.urlRoot = data.urlRoot || _config.default.urlRoot;\n _config.default.csrfNonce = data.csrfNonce || _config.default.csrfNonce;\n _config.default.userMode = data.userMode || _config.default.userMode;\n api.domain = _config.default.urlRoot + \"/api/v1\";\n user.id = data.userId;\n};\n\nvar plugin = {\n run: function run(f) {\n f(CTFd);\n }\n};\n\nfunction markdown(config) {\n // Merge passed config with original. Default to original.\n var md_config = _objectSpread({}, {\n html: true,\n linkify: true\n }, {}, config);\n\n var md = (0, _markdownIt.default)(md_config);\n\n md.renderer.rules.link_open = function (tokens, idx, options, env, self) {\n tokens[idx].attrPush([\"target\", \"_blank\"]);\n return self.renderToken(tokens, idx, options);\n };\n\n return md;\n}\n\nvar CTFd = {\n init: init,\n config: _config.default,\n fetch: _fetch.default,\n user: user,\n ui: ui,\n api: api,\n lib: lib,\n _internal: _internal,\n plugin: plugin\n};\nvar _default = CTFd;\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/CTFd.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -162,7 +162,7 @@
|
|||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/core/assets/js/pages/main.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _momentTimezone = _interopRequireDefault(__webpack_require__(/*! moment-timezone */ \"./node_modules/moment-timezone/index.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! ../CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction switchTab(event) {\n event.preventDefault(); // Handle tab validation\n\n var valid_tab = true;\n (0, _jquery.default)(event.target).closest(\"[role=tabpanel]\").find(\"input,textarea\").each(function (i, e) {\n $e = (0, _jquery.default)(e);\n var status = e.checkValidity();\n\n if (status === false) {\n $e.removeClass(\"input-filled-valid\");\n $e.addClass(\"input-filled-invalid\");\n valid_tab = false;\n }\n });\n\n if (valid_tab == false) {\n return;\n }\n\n var href = (0, _jquery.default)(event.target).data(\"href\");\n (0, _jquery.default)(\".nav a[href=\\\"\".concat(href, \"\\\"]\")).tab(\"show\");\n}\n\nfunction processDateTime(datetime) {\n var date_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-date\"));\n var time_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-time\"));\n return function (event) {\n var unix_time = (0, _momentTimezone.default)(\"\".concat(date_picker.val(), \" \").concat(time_picker.val()), \"YYYY-MM-DD HH:mm\").utc().format(\"X\");\n (0, _jquery.default)(\"#\".concat(datetime, \"-preview\")).val(unix_time);\n };\n}\n\nfunction mlcSetup(event) {\n var params = {\n name: (0, _jquery.default)(\"#ctf_name\").val(),\n type: \"jeopardy\",\n description: (0, _jquery.default)(\"#ctf_description\").val(),\n user_mode: (0, _jquery.default)(\"#user_mode\").val(),\n event_url: window.location.origin + _CTFd.default.config.urlRoot,\n redirect_url: window.location.origin + _CTFd.default.config.urlRoot + \"/redirect\",\n integration_setup_url: window.location.origin + _CTFd.default.config.urlRoot + \"/setup/integrations\",\n start: (0, _jquery.default)(\"#start-preview\").val(),\n end: (0, _jquery.default)(\"#end-preview\").val(),\n platform: \"CTFd\",\n state: STATE\n };\n var ret = [];\n\n for (var p in params) {\n ret.push(encodeURIComponent(p) + \"=\" + encodeURIComponent(params[p]));\n }\n\n window.open(\"https://www.majorleaguecyber.org/events/new?\" + ret.join(\"&\"), \"_blank\");\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".tab-next\").click(switchTab);\n (0, _jquery.default)(\"input\").on(\"keypress\", function (e) {\n // Hook Enter button\n if (e.keyCode == 13) {\n e.preventDefault();\n (0, _jquery.default)(e.target).closest(\".tab-pane\").find(\"button[data-href]\").click();\n }\n });\n (0, _jquery.default)(\"#integration-mlc\").click(mlcSetup);\n (0, _jquery.default)(\"#start-date,#start-time\").change(processDateTime(\"start\"));\n (0, _jquery.default)(\"#end-date,#end-time\").change(processDateTime(\"end\"));\n (0, _jquery.default)(\"#config-color-picker\").on(\"input\", function (e) {\n (0, _jquery.default)(\"#config-color-input\").val((0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(\"#config-color-reset\").click(function () {\n (0, _jquery.default)(\"#config-color-input\").val(\"\");\n (0, _jquery.default)(\"#config-color-picker\").val(\"\");\n });\n window.addEventListener(\"storage\", function (event) {\n if (event.key == \"integrations\" && event.newValue) {\n var integration = JSON.parse(event.newValue);\n\n if (integration[\"name\"] == \"mlc\") {\n (0, _jquery.default)(\"#integration-mlc\").text(\"Already Configured\").attr(\"disabled\", true);\n window.focus();\n localStorage.removeItem(\"integrations\");\n }\n }\n });\n});\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/pages/setup.js?");
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/core/assets/js/pages/main.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _momentTimezone = _interopRequireDefault(__webpack_require__(/*! moment-timezone */ \"./node_modules/moment-timezone/index.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! ../CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction switchTab(event) {\n event.preventDefault(); // Handle tab validation\n\n var valid_tab = true;\n (0, _jquery.default)(event.target).closest(\"[role=tabpanel]\").find(\"input,textarea\").each(function (i, e) {\n $e = (0, _jquery.default)(e);\n var status = e.checkValidity();\n\n if (status === false) {\n $e.removeClass(\"input-filled-valid\");\n $e.addClass(\"input-filled-invalid\");\n valid_tab = false;\n }\n });\n\n if (valid_tab == false) {\n return;\n }\n\n var href = (0, _jquery.default)(event.target).data(\"href\");\n (0, _jquery.default)(\".nav a[href=\\\"\".concat(href, \"\\\"]\")).tab(\"show\");\n}\n\nfunction processDateTime(datetime) {\n var date_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-date\"));\n var time_picker = (0, _jquery.default)(\"#\".concat(datetime, \"-time\"));\n return function (event) {\n var unix_time = (0, _momentTimezone.default)(\"\".concat(date_picker.val(), \" \").concat(time_picker.val()), \"YYYY-MM-DD HH:mm\").utc().format(\"X\");\n (0, _jquery.default)(\"#\".concat(datetime, \"-preview\")).val(unix_time);\n };\n}\n\nfunction mlcSetup(event) {\n var params = {\n name: (0, _jquery.default)(\"#ctf_name\").val(),\n type: \"jeopardy\",\n description: (0, _jquery.default)(\"#ctf_description\").val(),\n user_mode: (0, _jquery.default)(\"#user_mode\").val(),\n event_url: window.location.origin + _CTFd.default.config.urlRoot,\n redirect_url: window.location.origin + _CTFd.default.config.urlRoot + \"/redirect\",\n integration_setup_url: window.location.origin + _CTFd.default.config.urlRoot + \"/setup/integrations\",\n start: (0, _jquery.default)(\"#start-preview\").val(),\n end: (0, _jquery.default)(\"#end-preview\").val(),\n platform: \"CTFd\",\n state: STATE\n };\n var ret = [];\n\n for (var p in params) {\n ret.push(encodeURIComponent(p) + \"=\" + encodeURIComponent(params[p]));\n }\n\n window.open(\"https://www.majorleaguecyber.org/events/new?\" + ret.join(\"&\"), \"_blank\");\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".tab-next\").click(switchTab);\n (0, _jquery.default)(\"input\").on(\"keypress\", function (e) {\n // Hook Enter button\n if (e.keyCode == 13) {\n e.preventDefault();\n (0, _jquery.default)(e.target).closest(\".tab-pane\").find(\"button[data-href]\").click();\n }\n });\n (0, _jquery.default)(\"#integration-mlc\").click(mlcSetup);\n (0, _jquery.default)(\"#start-date,#start-time\").change(processDateTime(\"start\"));\n (0, _jquery.default)(\"#end-date,#end-time\").change(processDateTime(\"end\"));\n (0, _jquery.default)(\"#config-color-picker\").on(\"input\", function (e) {\n (0, _jquery.default)(\"#config-color-input\").val((0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(\"#config-color-reset\").click(function () {\n (0, _jquery.default)(\"#config-color-input\").val(\"\");\n (0, _jquery.default)(\"#config-color-picker\").val(\"\");\n });\n window.addEventListener(\"storage\", function (event) {\n if (event.key == \"integrations\" && event.newValue) {\n var integration = JSON.parse(event.newValue);\n\n if (integration[\"name\"] == \"mlc\") {\n (0, _jquery.default)(\"#integration-mlc\").text(\"Already Configured\").attr(\"disabled\", true);\n window.focus();\n localStorage.removeItem(\"integrations\");\n }\n }\n });\n (0, _jquery.default)(\"#setup-form\").submit(function (e) {\n if ((0, _jquery.default)(\"#newsletter-checkbox\").prop(\"checked\")) {\n var email = (0, _jquery.default)(e.target).find(\"input[name=email]\").val();\n\n _jquery.default.ajax({\n type: \"POST\",\n url: \"https://ctfd.us15.list-manage.com/subscribe/post-json?u=6c7fa6feeced52775aec9d015&id=dd1484208e&c=?\",\n data: {\n EMAIL: email,\n subscribe: \"Subscribe\",\n b_6c7fa6feeced52775aec9d015_dd1484208e: \"\"\n },\n dataType: \"jsonp\",\n contentType: \"application/json; charset=utf-8\"\n });\n }\n });\n});\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/pages/setup.js?");
|
||||
|
||||
/***/ })
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -18,7 +18,6 @@
|
|||
<link rel="stylesheet" type="text/css" href="{{ stylesheet }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('views.custom_css') }}">
|
||||
<script type="text/javascript">
|
||||
var init = {
|
||||
'urlRoot': "{{ request.script_root }}",
|
||||
|
@ -29,6 +28,7 @@
|
|||
'end': {{ get_config("end") | tojson }},
|
||||
}
|
||||
</script>
|
||||
{{ get_config('theme_header', '') | safe }}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
|
@ -210,5 +210,7 @@
|
|||
<script defer src="{{ script }}"></script>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{{ get_config('theme_footer', '') | safe }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal">
|
||||
<form method="post" accept-charset="utf-8" autocomplete="off" role="form" class="form-horizontal" id="setup-form">
|
||||
<ul class="nav nav-pills nav-fill mb-4">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#general">General</a>
|
||||
|
@ -111,6 +111,14 @@
|
|||
</label>
|
||||
<input class="form-control" type="password" name="password" required/>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" value="" id="newsletter-checkbox" checked>
|
||||
Subscribe email address to the CTFd LLC Newsletter for news and updates
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="float-right">
|
||||
<button type="button" class="btn btn-primary btn-outlined tab-next" data-href="#style">
|
||||
Next
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from flask import request, render_template, Blueprint
|
||||
from flask import Blueprint, render_template, request
|
||||
|
||||
from CTFd.models import Users
|
||||
from CTFd.utils.decorators import authed_only
|
||||
from CTFd.utils import config
|
||||
from CTFd.utils.user import get_current_user
|
||||
from CTFd.utils.decorators import authed_only
|
||||
from CTFd.utils.decorators.visibility import (
|
||||
check_account_visibility,
|
||||
check_score_visibility,
|
||||
)
|
||||
from CTFd.utils.user import get_current_user
|
||||
|
||||
users = Blueprint("users", __name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import mistune
|
||||
import six
|
||||
from flask import current_app as app
|
||||
|
||||
from CTFd.cache import cache
|
||||
|
||||
if six.PY2:
|
||||
|
@ -59,4 +60,4 @@ def set_config(key, value):
|
|||
return config
|
||||
|
||||
|
||||
from CTFd.models import db, Configs # noqa: E402
|
||||
from CTFd.models import Configs, db # noqa: E402
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from flask import current_app as app
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.modes import USERS_MODE, TEAMS_MODE
|
||||
import time
|
||||
import os
|
||||
import time
|
||||
|
||||
from flask import current_app as app
|
||||
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.modes import TEAMS_MODE, USERS_MODE
|
||||
|
||||
|
||||
def ctf_name():
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from CTFd.utils import string_types
|
||||
from passlib.hash import bcrypt_sha256
|
||||
import hashlib
|
||||
|
||||
from passlib.hash import bcrypt_sha256
|
||||
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
def hash_password(plaintext):
|
||||
return bcrypt_sha256.hash(str(plaintext))
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from CTFd.utils import get_config
|
||||
|
||||
import datetime
|
||||
import time
|
||||
|
||||
from CTFd.utils import get_config
|
||||
|
||||
|
||||
def ctftime():
|
||||
""" Checks whether it's CTF time or not. """
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
from flask import request, redirect, url_for, abort, jsonify
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.cache import cache
|
||||
from CTFd.utils.dates import ctf_ended, ctf_started, ctftime, view_after_ctf
|
||||
from CTFd.utils import user as current_user
|
||||
from CTFd.utils.user import get_current_team, is_admin, authed
|
||||
from CTFd.utils.modes import TEAMS_MODE
|
||||
import functools
|
||||
|
||||
from flask import abort, jsonify, redirect, request, url_for
|
||||
|
||||
from CTFd.cache import cache
|
||||
from CTFd.utils import config, get_config
|
||||
from CTFd.utils import user as current_user
|
||||
from CTFd.utils.dates import ctf_ended, ctf_started, ctftime, view_after_ctf
|
||||
from CTFd.utils.modes import TEAMS_MODE
|
||||
from CTFd.utils.user import authed, get_current_team, is_admin
|
||||
|
||||
|
||||
def during_ctf_time_only(f):
|
||||
"""
|
||||
|
@ -84,7 +86,10 @@ def authed_only(f):
|
|||
if authed():
|
||||
return f(*args, **kwargs)
|
||||
else:
|
||||
if request.content_type == "application/json" or request.accept_mimetypes.best == "text/event-stream":
|
||||
if (
|
||||
request.content_type == "application/json"
|
||||
or request.accept_mimetypes.best == "text/event-stream"
|
||||
):
|
||||
abort(403)
|
||||
else:
|
||||
return redirect(url_for("auth.login", next=request.full_path))
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from CTFd.utils.modes import TEAMS_MODE, USERS_MODE
|
||||
from CTFd.utils import get_config
|
||||
from flask import abort
|
||||
import functools
|
||||
|
||||
from flask import abort
|
||||
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.modes import TEAMS_MODE, USERS_MODE
|
||||
|
||||
|
||||
def require_team_mode(f):
|
||||
@functools.wraps(f)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from flask import request, abort, redirect, url_for, render_template
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.user import is_admin, authed
|
||||
import functools
|
||||
|
||||
from flask import abort, redirect, render_template, request, url_for
|
||||
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.user import authed, is_admin
|
||||
|
||||
|
||||
def check_score_visibility(f):
|
||||
@functools.wraps(f)
|
||||
|
|
|
@ -1,10 +1,38 @@
|
|||
from flask import url_for
|
||||
|
||||
from CTFd.utils import get_config
|
||||
from CTFd.utils.formatters import safe_format
|
||||
from CTFd.utils.config import get_mail_provider
|
||||
from CTFd.utils.email import mailgun, smtp
|
||||
from CTFd.utils.formatters import safe_format
|
||||
from CTFd.utils.security.signing import serialize
|
||||
|
||||
DEFAULT_VERIFICATION_EMAIL_SUBJECT = "Confirm your account for {ctf_name}"
|
||||
DEFAULT_VERIFICATION_EMAIL_BODY = (
|
||||
"Please click the following link to confirm your email "
|
||||
"address for {ctf_name}: {url}"
|
||||
)
|
||||
DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_SUBJECT = "Successfully registered for {ctf_name}"
|
||||
DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_BODY = (
|
||||
"You've successfully registered for {ctf_name}!"
|
||||
)
|
||||
DEFAULT_USER_CREATION_EMAIL_SUBJECT = "Message from {ctf_name}"
|
||||
DEFAULT_USER_CREATION_EMAIL_BODY = (
|
||||
"An account has been created for you for {ctf_name} at {url}. \n\n"
|
||||
"Username: {name}\n"
|
||||
"Password: {password}"
|
||||
)
|
||||
DEFAULT_PASSWORD_RESET_SUBJECT = "Password Reset Request from {ctf_name}"
|
||||
DEFAULT_PASSWORD_RESET_BODY = (
|
||||
"Did you initiate a password reset? "
|
||||
"If you didn't initiate this request you can ignore this email. \n\n"
|
||||
"Click the following link to reset your password:\n{url}"
|
||||
)
|
||||
DEFAULT_PASSWORD_CHANGE_ALERT_SUBJECT = "Password Change Confirmation for {ctf_name}"
|
||||
DEFAULT_PASSWORD_CHANGE_ALERT_BODY = (
|
||||
"Your password for {ctf_name} has been changed.\n\n"
|
||||
"If you didn't request a password change you can reset your password here: {url}"
|
||||
)
|
||||
|
||||
|
||||
def sendmail(addr, text, subject="Message from {ctf_name}"):
|
||||
subject = safe_format(subject, ctf_name=get_config("ctf_name"))
|
||||
|
@ -17,48 +45,84 @@ def sendmail(addr, text, subject="Message from {ctf_name}"):
|
|||
|
||||
|
||||
def password_change_alert(email):
|
||||
ctf_name = get_config("ctf_name")
|
||||
text = (
|
||||
"Your password for {ctf_name} has been changed.\n\n"
|
||||
"If you didn't request a password change you can reset your password here: {url}"
|
||||
).format(ctf_name=ctf_name, url=url_for("auth.reset_password", _external=True))
|
||||
text = safe_format(
|
||||
get_config("password_change_alert_body") or DEFAULT_PASSWORD_CHANGE_ALERT_BODY,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
ctf_description=get_config("ctf_description"),
|
||||
url=url_for("auth.reset_password", _external=True),
|
||||
)
|
||||
|
||||
subject = "Password Change Confirmation for {ctf_name}".format(ctf_name=ctf_name)
|
||||
subject = safe_format(
|
||||
get_config("password_change_alert_subject")
|
||||
or DEFAULT_PASSWORD_CHANGE_ALERT_SUBJECT,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
)
|
||||
return sendmail(addr=email, text=text, subject=subject)
|
||||
|
||||
|
||||
def forgot_password(email):
|
||||
token = serialize(email)
|
||||
text = """Did you initiate a password reset? If you didn't initiate this request you can ignore this email.
|
||||
|
||||
Click the following link to reset your password:
|
||||
{0}/{1}
|
||||
""".format(
|
||||
url_for("auth.reset_password", _external=True), token
|
||||
text = safe_format(
|
||||
get_config("password_reset_body") or DEFAULT_PASSWORD_RESET_BODY,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
ctf_description=get_config("ctf_description"),
|
||||
url=url_for("auth.reset_password", data=serialize(email), _external=True),
|
||||
)
|
||||
|
||||
subject = safe_format(
|
||||
get_config("password_reset_subject") or DEFAULT_PASSWORD_RESET_SUBJECT,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
)
|
||||
subject = "Password Reset Request from {ctf_name}"
|
||||
return sendmail(addr=email, text=text, subject=subject)
|
||||
|
||||
|
||||
def verify_email_address(addr):
|
||||
token = serialize(addr)
|
||||
text = """Please click the following link to confirm your email address for {ctf_name}: {url}/{token}""".format(
|
||||
text = safe_format(
|
||||
get_config("verification_email_body") or DEFAULT_VERIFICATION_EMAIL_BODY,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
ctf_description=get_config("ctf_description"),
|
||||
url=url_for("auth.confirm", data=serialize(addr), _external=True),
|
||||
)
|
||||
|
||||
subject = safe_format(
|
||||
get_config("verification_email_subject") or DEFAULT_VERIFICATION_EMAIL_SUBJECT,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
)
|
||||
return sendmail(addr=addr, text=text, subject=subject)
|
||||
|
||||
|
||||
def successful_registration_notification(addr):
|
||||
text = safe_format(
|
||||
get_config("successful_registration_email_body")
|
||||
or DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_BODY,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
ctf_description=get_config("ctf_description"),
|
||||
url=url_for("views.static_html", _external=True),
|
||||
)
|
||||
|
||||
subject = safe_format(
|
||||
get_config("successful_registration_email_subject")
|
||||
or DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_SUBJECT,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
url=url_for("auth.confirm", _external=True),
|
||||
token=token,
|
||||
)
|
||||
subject = "Confirm your account for {ctf_name}"
|
||||
return sendmail(addr=addr, text=text, subject=subject)
|
||||
|
||||
|
||||
def user_created_notification(addr, name, password):
|
||||
text = """An account has been created for you for {ctf_name} at {url}. \n\nUsername: {name}\nPassword: {password}""".format(
|
||||
text = safe_format(
|
||||
get_config("user_creation_email_body") or DEFAULT_USER_CREATION_EMAIL_BODY,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
ctf_description=get_config("ctf_description"),
|
||||
url=url_for("views.static_html", _external=True),
|
||||
name=name,
|
||||
password=password,
|
||||
)
|
||||
return sendmail(addr, text)
|
||||
|
||||
subject = safe_format(
|
||||
get_config("user_creation_email_subject")
|
||||
or DEFAULT_USER_CREATION_EMAIL_SUBJECT,
|
||||
ctf_name=get_config("ctf_name"),
|
||||
)
|
||||
return sendmail(addr=addr, text=text, subject=subject)
|
||||
|
||||
|
||||
def check_email_is_whitelisted(email_address):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from CTFd.utils import get_config, get_app_config
|
||||
import requests
|
||||
|
||||
from CTFd.utils import get_app_config, get_config
|
||||
|
||||
|
||||
def sendmail(addr, text, subject):
|
||||
ctf_name = get_config("ctf_name")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from CTFd.utils import get_config, get_app_config
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from socket import timeout
|
||||
import smtplib
|
||||
|
||||
from CTFd.utils import get_app_config, get_config
|
||||
|
||||
|
||||
def get_smtp(host, port, username=None, password=None, TLS=None, SSL=None, auth=None):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from CTFd.utils import string_types
|
||||
import base64
|
||||
import codecs
|
||||
|
||||
import six
|
||||
import codecs
|
||||
import base64
|
||||
|
||||
from CTFd.utils import string_types
|
||||
|
||||
|
||||
def hexencode(s):
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue