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 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 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 create_challenge(**kwargs): """ Creates a challenge given parameters in application/json body """ data = request.form.to_dict() category = Category.query.filter_by(name=data['category'].lower()).first() difficulty = Difficulty.query.filter_by(name=data['difficulty'].lower()).first() if not (category and difficulty): return jsonify({ 'status': "error", 'message': "The category or difficulty doesn't exist" }), 422 submitter = kwargs['userinfo'].get('preferred_username') if not submitter: return no_username() # check if challenge with matching title already exists if Challenge.query.filter(Challenge.title.ilike('test')).first(): return collision() file = request.files.get('file') if category.upload_required and not file: return missing_body_parts("multipart/form-data", "file") else: split = os.path.splitext(secure_filename(file.filename)) filename = secure_filename(data['title'] + str(split[-1])) filepath = os.path.join(app.config['UPLOAD_PATH'], filename) file.save(filepath) mime = magic.Magic(mime=True) if not mime.from_file(filepath) in app.config['ALLOWED_MIME_TYPES']: return invalid_mime_type(mime.from_file(filepath)) tags = [] if tag_names := data.get('tags'): tags = list(dict.fromkeys(tag_names.split(",")))
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
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
offset = 1 search_query = request.args.get('search') sort_by = request.args.get('sort_by') order_by = request.args.get('order_by') # Category and Difficulty parameters are a comma-separated list of categories/difficulties categories = [] difficulties = [] if category_names := request.args.get('categories'): categories = category_names.split(',') if difficulty_names := request.args.get('difficulties'): difficulties = difficulty_names.split(',') current_user = kwargs['userinfo'].get('preferred_username') if not current_user: return no_username() challenges = Challenge.query if categories: challenges = challenges.filter(Challenge.category_name.in_(categories)) if difficulties: challenges = challenges.filter(Challenge.difficulty_name.in_(difficulties)) if search_query: challenges = challenges.filter( getattr(Challenge, 'description').ilike(f'%{search_query}%') | getattr(Challenge, 'title').ilike(f'%{search_query}%') | getattr(Challenge, 'submitter').ilike(f'%{search_query}%') ) order_op = asc if order_by == "asc" else desc if sort_by in ('date', 'ts'): sort_query = order_op(Challenge.ts)