diff --git a/CTFd/challenges.py b/CTFd/challenges.py index a13278d0..e61d84e6 100644 --- a/CTFd/challenges.py +++ b/CTFd/challenges.py @@ -1,14 +1,12 @@ -from flask import Blueprint, render_template +from flask import Blueprint, redirect, render_template, request, url_for -from CTFd.utils import config +from CTFd.constants.config import ChallengeVisibilityTypes, Configs +from CTFd.utils.config import is_teams_mode from CTFd.utils.dates import ctf_ended, ctf_paused, ctf_started -from CTFd.utils.decorators import ( - during_ctf_time_only, - require_team, - require_verified_emails, -) +from CTFd.utils.decorators import during_ctf_time_only, require_verified_emails from CTFd.utils.decorators.visibility import check_challenge_visibility from CTFd.utils.helpers import get_errors, get_infos +from CTFd.utils.user import authed, get_current_team challenges = Blueprint("challenges", __name__) @@ -17,18 +15,26 @@ challenges = Blueprint("challenges", __name__) @during_ctf_time_only @require_verified_emails @check_challenge_visibility -@require_team def listing(): + if ( + Configs.challenge_visibility == ChallengeVisibilityTypes.PUBLIC + and authed() is False + ): + pass + else: + if is_teams_mode() and get_current_team() is None: + return redirect(url_for("teams.private", next=request.full_path)) + infos = get_infos() errors = get_errors() if ctf_started() is False: - errors.append(f"{config.ctf_name()} has not started yet") + errors.append(f"{Configs.ctf_name} has not started yet") if ctf_paused() is True: - infos.append(f"{config.ctf_name()} is paused") + infos.append(f"{Configs.ctf_name} is paused") if ctf_ended() is True: - infos.append(f"{config.ctf_name()} has ended") + infos.append(f"{Configs.ctf_name} has ended") return render_template("challenges.html", infos=infos, errors=errors) diff --git a/tests/teams/test_challenges.py b/tests/teams/test_challenges.py index 0d4c5c30..a5599e90 100644 --- a/tests/teams/test_challenges.py +++ b/tests/teams/test_challenges.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from CTFd.utils import set_config from CTFd.utils.scores import get_standings from tests.helpers import ( create_ctfd, @@ -10,6 +11,7 @@ from tests.helpers import ( gen_team, gen_user, login_as_user, + register_user, ) @@ -38,3 +40,26 @@ def test_challenge_team_submit(): assert standings[0][2] == "team_name" assert standings[0][3] == 100 destroy_ctfd(app) + + +def test_anonymous_users_view_public_challenges_without_team(): + """Test that if challenges are public, users without team can still view them""" + app = create_ctfd(user_mode="teams") + with app.app_context(): + register_user(app) + gen_challenge(app.db) + with app.test_client() as client: + r = client.get("/challenges") + assert r.status_code == 302 + assert r.location.startswith("http://localhost/login") + + set_config("challenge_visibility", "public") + with app.test_client() as client: + r = client.get("/challenges") + assert r.status_code == 200 + + with login_as_user(app) as client: + r = client.get("/challenges") + assert r.status_code == 302 + assert r.location.startswith("http://localhost/team") + destroy_ctfd(app)