def join(): infos = get_infos() errors = get_errors() user = get_current_user_attrs() if user.team_id: errors.append("You are already in a team. You cannot join another.") if request.method == "GET": team_size_limit = get_config("team_size", default=0) if team_size_limit: plural = "" if team_size_limit == 1 else "s" infos.append("Teams are limited to {limit} member{plural}".format( limit=team_size_limit, plural=plural)) return render_template("teams/join_team.html", infos=infos, errors=errors) if request.method == "POST": teamname = request.form.get("name") passphrase = request.form.get("password", "").strip() team = Teams.query.filter_by(name=teamname).first() if errors: return ( render_template("teams/join_team.html", infos=infos, errors=errors), 403, ) if team and verify_password(passphrase, team.password): team_size_limit = get_config("team_size", default=0) if team_size_limit and len(team.members) >= team_size_limit: errors.append( "{name} has already reached the team size limit of {limit}" .format(name=team.name, limit=team_size_limit)) return render_template("teams/join_team.html", infos=infos, errors=errors) user = get_current_user() user.team_id = team.id db.session.commit() if len(team.members) == 1: team.captain_id = user.id db.session.commit() clear_user_session(user_id=user.id) clear_team_session(team_id=team.id) return redirect(url_for("challenges.listing")) else: errors.append("That information is incorrect") return render_template("teams/join_team.html", infos=infos, errors=errors)
def banned(): if request.endpoint == "views.themes": return if authed(): user = get_current_user_attrs() team = get_current_team_attrs() if user and user.banned: return ( render_template( "errors/403.html", error="You have been banned from this CTF"), 403, ) if team and team.banned: return ( render_template( "errors/403.html", error="Your team has been banned from this CTF", ), 403, )
def get(self, query_args): # Require a team if in teams mode # TODO: Convert this into a re-useable decorator # TODO: The require_team decorator doesnt work because of no admin passthru if get_current_user_attrs(): if is_admin(): pass else: if config.is_teams_mode() and get_current_team_attrs() is None: abort(403) # Build filtering queries q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Challenges, query=q, field=field) # Admins get a shortcut to see all challenges despite pre-requisites admin_view = is_admin() and request.args.get("view") == "admin" solve_counts = {} # Build a query for to show challenge solve information. We only # give an admin view if the request argument has been provided. # # NOTE: This is different behaviour to the challenge detail # endpoint which only needs the current user to be an admin rather # than also also having to provide `view=admin` as a query arg. solves_q, user_solves = _build_solves_query(admin_view=admin_view) # Aggregate the query results into the hashes defined at the top of # this block for later use for chal_id, solve_count in solves_q: solve_counts[chal_id] = solve_count if scores_visible() and accounts_visible(): solve_count_dfl = 0 else: # Empty out the solves_count if we're hiding scores/accounts solve_counts = {} # This is necessary to match the challenge detail API which returns # `None` for the solve count if visiblity checks fail solve_count_dfl = None # Build the query for the challenges which may be listed chal_q = Challenges.query # Admins can see hidden and locked challenges in the admin view if admin_view is False: chal_q = chal_q.filter( and_(Challenges.state != "hidden", Challenges.state != "locked")) chal_q = (chal_q.filter_by(**query_args).filter(*filters).order_by( Challenges.value, Challenges.id)) # Iterate through the list of challenges, adding to the object which # will be JSONified back to the client response = [] tag_schema = TagSchema(view="user", many=True) # Gather all challenge IDs so that we can determine invalid challenge prereqs all_challenge_ids = { c.id for c in Challenges.query.with_entities(Challenges.id).all() } for challenge in chal_q: if challenge.requirements: requirements = challenge.requirements.get("prerequisites", []) anonymize = challenge.requirements.get("anonymize") prereqs = set(requirements).intersection(all_challenge_ids) if user_solves >= prereqs or admin_view: pass else: if anonymize: response.append({ "id": challenge.id, "type": "hidden", "name": "???", "value": 0, "solves": None, "solved_by_me": False, "category": "???", "tags": [], "template": "", "script": "", }) # Fallthrough to continue continue try: challenge_type = get_chal_class(challenge.type) except KeyError: # Challenge type does not exist. Fall through to next challenge. continue # Challenge passes all checks, add it to response response.append({ "id": challenge.id, "type": challenge_type.name, "name": challenge.name, "value": challenge.value, "solves": solve_counts.get(challenge.id, solve_count_dfl), "solved_by_me": challenge.id in user_solves, "category": challenge.category, "tags": tag_schema.dump(challenge.tags).data, "template": challenge_type.templates["view"], "script": challenge_type.scripts["view"], }) db.session.close() return {"success": True, "data": response}
def invite(): infos = get_infos() errors = get_errors() code = request.args.get("code") if code is None: abort(404) user = get_current_user_attrs() if user.team_id: errors.append("You are already in a team. You cannot join another.") try: team = Teams.load_invite_code(code) except TeamTokenExpiredException: abort(403, description="This invite URL has expired") except TeamTokenInvalidException: abort(403, description="This invite URL is invalid") team_size_limit = get_config("team_size", default=0) if request.method == "GET": if team_size_limit: infos.append( "Teams are limited to {limit} member{plural}".format( limit=team_size_limit, plural=pluralize(number=team_size_limit) ) ) return render_template( "teams/invite.html", team=team, infos=infos, errors=errors ) if request.method == "POST": if errors: return ( render_template( "teams/invite.html", team=team, infos=infos, errors=errors ), 403, ) if team_size_limit and len(team.members) >= team_size_limit: errors.append( "{name} has already reached the team size limit of {limit}".format( name=team.name, limit=team_size_limit ) ) return ( render_template( "teams/invite.html", team=team, infos=infos, errors=errors ), 403, ) user = get_current_user() user.team_id = team.id db.session.commit() clear_user_session(user_id=user.id) clear_team_session(team_id=team.id) return redirect(url_for("challenges.listing"))
def new(): infos = get_infos() errors = get_errors() if bool(get_config("team_creation", default=True)) is False: abort( 403, description="Team creation is currently disabled. Please join an existing team.", ) num_teams_limit = int(get_config("num_teams", default=0)) num_teams = Teams.query.filter_by(banned=False, hidden=False).count() if num_teams_limit and num_teams >= num_teams_limit: abort( 403, description=f"Reached the maximum number of teams ({num_teams_limit}). Please join an existing team.", ) user = get_current_user_attrs() if user.team_id: errors.append("You are already in a team. You cannot join another.") if request.method == "GET": team_size_limit = get_config("team_size", default=0) if team_size_limit: plural = "" if team_size_limit == 1 else "s" infos.append( "Teams are limited to {limit} member{plural}".format( limit=team_size_limit, plural=plural ) ) return render_template("teams/new_team.html", infos=infos, errors=errors) elif request.method == "POST": teamname = request.form.get("name", "").strip() passphrase = request.form.get("password", "").strip() website = request.form.get("website") affiliation = request.form.get("affiliation") user = get_current_user() existing_team = Teams.query.filter_by(name=teamname).first() if existing_team: errors.append("That team name is already taken") if not teamname: errors.append("That team name is invalid") # Process additional user fields fields = {} for field in TeamFields.query.all(): fields[field.id] = field entries = {} for field_id, field in fields.items(): value = request.form.get(f"fields[{field_id}]", "").strip() if field.required is True and (value is None or value == ""): errors.append("Please provide all required fields") break # Handle special casing of existing profile fields if field.name.lower() == "affiliation": affiliation = value break elif field.name.lower() == "website": website = value break if field.field_type == "boolean": entries[field_id] = bool(value) else: entries[field_id] = value if website: valid_website = validators.validate_url(website) else: valid_website = True if affiliation: valid_affiliation = len(affiliation) < 128 else: valid_affiliation = True if valid_website is False: errors.append("Websites must be a proper URL starting with http or https") if valid_affiliation is False: errors.append("Please provide a shorter affiliation") if errors: return render_template("teams/new_team.html", errors=errors), 403 team = Teams(name=teamname, password=passphrase, captain_id=user.id) if website: team.website = website if affiliation: team.affiliation = affiliation db.session.add(team) db.session.commit() for field_id, value in entries.items(): entry = TeamFieldEntries(field_id=field_id, value=value, team_id=team.id) db.session.add(entry) db.session.commit() user.team_id = team.id db.session.commit() clear_user_session(user_id=user.id) clear_team_session(team_id=team.id) return redirect(url_for("challenges.listing"))
def __getattr__(self, attr): from CTFd.utils.user import get_current_user_attrs attrs = get_current_user_attrs() return getattr(attrs, attr, None)