def delete_challenge(challenge_id: int, **kwargs): """ Deletes the specified challenge """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() groups = kwargs['userinfo'].get('groups') if (current_username != challenge.submitter) and (not is_ctf_admin(groups)): return not_authorized() if challenge.filename: threading.Thread( target=delete_s3_object, args=(challenge.filename,) ).start() delete_challenge_tags(challenge.id) delete_flags(challenge.id) challenge.delete() return jsonify({ 'status': "success" }), 200
def all_tags(challenge_id: int): """ Operations pertaining to tags :GET: Returns a list of all tags for the challenge with 'challenge_id' """ # Ensure challenge exists challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() return jsonify([tag.tag for tag in challenge.tags]), 200
def get_category(category_name: str): """ Operations relating to a single category :param category_name: Name of the category requested :GET: Returns the category's values :DELETE: Deletes the category """ category = Category.query.filter_by(name=category_name.lower()).first() if not category: return not_found() return jsonify(category.to_dict()), 200
def delete_tag(challenge_id: int, tag_name: str, **kwargs): """ Deletes the specified tag """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() tag = ChallengeTag.query.filter(func.lower(ChallengeTag.tag) == func.lower(tag_name), ChallengeTag.challenge_id == challenge_id).first() if not tag: return not_found() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() groups = kwargs['userinfo'].get('groups') if current_username != challenge.submitter and not is_ctf_admin(groups): return not_authorized() tag.delete() return jsonify({ 'status': "success" }), 200
def single_challenge(challenge_id: int, **kwargs): """ Operations pertaining to a single challenge :GET: Get the challenge identified by 'challenge_id' :DELETE: Delete the challenge identified by 'challenge_id' """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() current_user = kwargs['userinfo'].get("preferred_username") if not current_user: return no_username() return jsonify(get_all_challenge_data(challenge.id, current_user)), 200
def create_hint(challenge_id: int = 0, flag_id: int = 0, **kwargs): # pylint: disable=unused-argument """ Creates a hint given parameters in the application/json body """ flag = Flag.query.filter_by(id=flag_id).first() if not flag: return not_found() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() data = request.get_json() new_hint = Hint.create(data['cost'], data['hint'], flag_id) return jsonify(new_hint), 201
def solved_flags(challenge_id: int): """ Operations pertaining to the solution of flags :GET: Get a list of the users who have solved each flag belonging to the specified challenge """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() flags = Flag.query.filter_by(challenge_id=challenge_id).all() response = dict() for flag in flags: solved = Solved.query.filter_by(flag_id=flag.id).all() response[flag.id] = [solution.username for solution in solved] return jsonify(response), 200
def delete_category(category_name: str): """ Delete the specified category """ category = Category.query.filter_by(name=category_name.lower()).first() if not category: return not_found() if Challenge.query.filter_by(category_name=category_name.lower()).first(): return jsonify({ 'status': "error", 'message': "You can't delete a category unless no challenges exist in that category" }), 409 category.delete() return jsonify({ 'status': "success" }), 200
def single_difficulty(difficulty_name: str): """ Deletes a difficulty """ difficulty = Difficulty.query.filter_by(name=difficulty_name.lower()).first() if not difficulty: return not_found() if Challenge.query.filter_by(difficulty_name=difficulty_name.lower()).first(): return jsonify({ 'status': "error", 'message': "You can't delete a difficulty that's being used by any challenges" }), 409 difficulty.delete() return jsonify({ 'status': "success" }), 200
def single_flag(challenge_id: int = 0, flag_id: int = 0, **kwargs): # pylint: disable=unused-argument """ Deletes the flag specified """ flag = Flag.query.filter_by(id=flag_id).first() if not flag: return not_found() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() groups = kwargs['userinfo'].get('groups') if current_username != flag.challenge.submitter and not is_ctf_admin( groups): return not_authorized() delete_flag(flag.id) return jsonify({'status': "success"}), 200
def purchase_hint(challenge_id: int = 0, flag_id: int = 0, hint_id: int = 0, **kwargs): # pylint: disable=unused-argument """ Operations relating to used hints :POST: Allow a user to pay for a hint """ hint = Hint.query.filter_by(id=hint_id).first() if not hint: return not_found() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() # Check that the relation doesn't already exist used_hints_check = UsedHint.query.filter_by(hint_id=hint_id, username=current_username).first() if used_hints_check: return collision() if current_username == hint.flag.challenge.submitter: return jsonify({ 'status': "error", 'message': "You created this hint!" }), 403 if Solved.query.filter_by(flag_id=hint.flag.id).first(): return jsonify({ 'status': "error", 'message': "You already solved the flag associated with this hint!" }), 422 if get_user_score(current_username)[0] - hint.cost < 0: return jsonify({ 'status': "error", 'message': "You don't have enough points to purchase this hint!" }), 422 new_used_hint = UsedHint.create(hint_id, current_username) return jsonify(new_used_hint.hint.to_dict()), 201
def single_tag(challenge_id: int, tag_name: str, **kwargs): """ Creates a tag """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() tag = ChallengeTag.query.filter_by(tag=tag_name, challenge_id=challenge_id).first() if tag: return collision() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() groups = kwargs['userinfo'].get('groups') if current_username != challenge.submitter and not is_ctf_admin(groups): return not_authorized() new_tag = ChallengeTag.create(challenge_id, tag_name) return jsonify(new_tag), 201
def all_hints(challenge_id: int, flag_id: int, **kwargs): # pylint: disable=unused-argument """ Operations relating to the hints objects :GET: Get all hints associated with the specified flag and challenge """ flag = Flag.query.filter_by(id=flag_id).first() if not flag: return not_found() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() # Delete a hint's data if a user hasn't unlocked it hints = [hint.to_dict() for hint in Hint.query.filter_by(flag_id=flag_id).all()] is_flag_creator = flag.challenge.submitter == current_username for hint in hints: if not is_flag_creator and \ not UsedHint.query.filter_by(hint_id=hint['id'], username=current_username).first(): del hint['hint'] return jsonify(hints), 200
def add_flag(challenge_id: int, **kwargs): """ Create a flag given parameters in application/json body """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() data = request.get_json() flag_exists = Flag.query.filter_by(challenge_id=challenge_id, flag=data['flag']).first() if flag_exists: return collision() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() groups = kwargs['userinfo'].get('groups') if current_username != challenge.submitter and not is_ctf_admin(groups): return not_authorized() new_flag = Flag.create(data['point_value'], data['flag'], challenge_id) return jsonify(new_flag), 201
def all_flags(challenge_id: int, **kwargs): """ Operations relating to flags :GET: Retrieves a list of flags associated with a challenge. The flag value is omitted if the person fetching hasn't yet solved the flag :POST: Creates a flag associated with a challenge """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() # If the person hasn't solved the flag, the flag data should be omitted. current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() used_hints = set(used_hint.hint_id for used_hint in UsedHint.query.filter_by( username=current_username).all()) flags = { flag.id: flag.to_dict() for flag in Flag.query.filter_by(challenge_id=challenge_id).all() } for flag in flags: is_creator = challenge.submitter == current_username if not is_creator and \ not Solved.query.filter_by(username=current_username, flag_id=flags[flag]['id']).first(): del flags[flag]['flag'] flags[flag]['hints'] = { hint.id: hint.to_dict() for hint in Hint.query.filter_by(flag_id=flag).all() } for hint in flags[flag]['hints'].values(): if not is_creator and hint['id'] not in used_hints: del flags[flag]['hints'][hint['id']]['hint'] return jsonify(flags), 200
def solve_flag(challenge_id: int, **kwargs): """ Operations pertaining to the solved relations on a challenge (but really a flag). :param challenge_id: The challenge that a solution is being attempted on :POST: Attempt solution of all flags associated with this challenge """ challenge = Challenge.query.filter_by(id=challenge_id).first() if not challenge: return not_found() data = request.get_json() flag_attempt = data['flag'] flags = Flag.query.filter_by(challenge_id=challenge_id).all() current_username = kwargs['userinfo'].get('preferred_username') if not current_username: return no_username() if challenge.submitter == current_username: return jsonify({ 'status': "error", 'message': "You created this flag!" }), 403 for flag in flags: if flag.flag == flag_attempt: check_solved = Solved.query.filter_by( flag_id=flag.id, username=current_username).first() if check_solved: return collision() Solved.create(flag.id, current_username) return jsonify(challenge.to_dict()), 201 return jsonify({'status': "error", 'message': "Incorrect flag"}), 400