class FieldList(Resource): @admins_only @validate_args( { "type": (str, None), "q": (str, None), "field": (RawEnum("FieldFields", {"description": "description"}), None), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Fields, query=q, field=field) fields = Fields.query.filter_by(**query_args).filter(*filters).all() schema = FieldSchema(many=True) response = schema.dump(fields) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only def post(self): req = request.get_json() schema = FieldSchema() response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() return {"success": True, "data": response.data}
class ChallengeList(Resource): @check_challenge_visibility @during_ctf_time_only @require_verified_emails @challenges_namespace.doc( description="Endpoint to get Challenge objects in bulk", responses={ 200: ("Success", "ChallengeListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "name": (str, None), "max_attempts": (int, None), "value": (int, None), "category": (str, None), "type": (str, None), "state": (str, None), "q": (str, None), "field": ( RawEnum( "ChallengeFields", { "name": "name", "description": "description", "category": "category", "type": "type", "state": "state", }, ), None, ), }, location="query", ) def get(self, query_args): # 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) # This can return None (unauth) if visibility is set to public user = get_current_user() # Admins can request to see everything if is_admin() and request.args.get("view") == "admin": challenges = (Challenges.query.filter_by(**query_args).filter( *filters).order_by(Challenges.value).all()) solve_ids = set([challenge.id for challenge in challenges]) else: challenges = (Challenges.query.filter( and_(Challenges.state != "hidden", Challenges.state != "locked")).filter_by( **query_args).filter(*filters).order_by( Challenges.value).all()) if user: solve_ids = (Solves.query.with_entities( Solves.challenge_id).filter_by( account_id=user.account_id).order_by( Solves.challenge_id.asc()).all()) solve_ids = set([value for value, in solve_ids]) # TODO: Convert this into a re-useable decorator if is_admin(): pass else: if config.is_teams_mode() and get_current_team() is None: abort(403) else: solve_ids = set() response = [] tag_schema = TagSchema(view="user", many=True) for challenge in challenges: if challenge.requirements: requirements = challenge.requirements.get("prerequisites", []) anonymize = challenge.requirements.get("anonymize") prereqs = set(requirements) if solve_ids >= prereqs: pass else: if anonymize: response.append({ "id": challenge.id, "type": "hidden", "name": "???", "value": 0, "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, "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} @admins_only @challenges_namespace.doc( description="Endpoint to create a Challenge object", responses={ 200: ("Success", "ChallengeDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): data = request.form or request.get_json() challenge_type = data["type"] challenge_class = get_chal_class(challenge_type) challenge = challenge_class.create(request) response = challenge_class.read(challenge) return {"success": True, "data": response}
class FilesList(Resource): @admins_only @files_namespace.doc( description="Endpoint to get file objects in bulk", responses={ 200: ("Success", "FileListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "type": (str, None), "location": (str, None), "q": (str, None), "field": ( RawEnum("FileFields", { "type": "type", "location": "location" }), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Files, query=q, field=field) files = Files.query.filter_by(**query_args).filter(*filters).all() schema = FileSchema(many=True) response = schema.dump(files) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @files_namespace.doc( description="Endpoint to get file objects in bulk", responses={ 200: ("Success", "FileDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): files = request.files.getlist("file") # challenge_id # page_id objs = [] for f in files: # uploads.upload_file(file=f, chalid=req.get('challenge')) obj = uploads.upload_file(file=f, **request.form.to_dict()) objs.append(obj) schema = FileSchema(many=True) response = schema.dump(objs) if response.errors: return {"success": False, "errors": response.errorss}, 400 return {"success": True, "data": response.data}
class ChallengeList(Resource): @check_challenge_visibility @during_ctf_time_only @require_verified_emails @challenges_namespace.doc( description="Endpoint to get Challenge objects in bulk", responses={ 200: ("Success", "ChallengeListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "name": (str, None), "max_attempts": (int, None), "value": (int, None), "category": (str, None), "type": (str, None), "state": (str, None), "q": (str, None), "field": ( RawEnum( "ChallengeFields", { "name": "name", "description": "description", "category": "category", "type": "type", "state": "state", }, ), None, ), }, location="query", ) 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} @admins_only @challenges_namespace.doc( description="Endpoint to create a Challenge object", responses={ 200: ("Success", "ChallengeDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): data = request.form or request.get_json() # Load data through schema for validation but not for insertion schema = ChallengeSchema() response = schema.load(data) if response.errors: return {"success": False, "errors": response.errors}, 400 challenge_type = data["type"] challenge_class = get_chal_class(challenge_type) challenge = challenge_class.create(request) response = challenge_class.read(challenge) return {"success": True, "data": response}
class NotificantionList(Resource): @notifications_namespace.doc( description="Endpoint to get notification objects in bulk", responses={ 200: ("Success", "NotificationListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "title": (str, None), "content": (str, None), "user_id": (int, None), "team_id": (int, None), "q": (str, None), "field": ( RawEnum("NotificationFields", { "title": "title", "content": "content" }), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Notifications, query=q, field=field) notifications = (Notifications.query.filter_by(**query_args).filter( *filters).all()) schema = NotificationSchema(many=True) result = schema.dump(notifications) if result.errors: return {"success": False, "errors": result.errors}, 400 return {"success": True, "data": result.data} @admins_only @notifications_namespace.doc( description="Endpoint to create a notification object", responses={ 200: ("Success", "NotificationDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() schema = NotificationSchema() result = schema.load(req) if result.errors: return {"success": False, "errors": result.errors}, 400 db.session.add(result.data) db.session.commit() response = schema.dump(result.data) # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data}
class UnlockList(Resource): @admins_only @unlocks_namespace.doc( description="Endpoint to get unlock objects in bulk", responses={ 200: ("Success", "UnlockListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "user_id": (int, None), "team_id": (int, None), "target": (int, None), "type": (str, None), "q": (str, None), "field": ( RawEnum("UnlockFields", { "target": "target", "type": "type" }), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Unlocks, query=q, field=field) unlocks = Unlocks.query.filter_by(**query_args).filter(*filters).all() schema = UnlockSchema() response = schema.dump(unlocks) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @during_ctf_time_only @require_verified_emails @authed_only @unlocks_namespace.doc( description= "Endpoint to create an unlock object. Used to unlock hints.", responses={ 200: ("Success", "UnlockDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() user = get_current_user() req["user_id"] = user.id req["team_id"] = user.team_id Model = get_class_by_tablename(req["type"]) target = Model.query.filter_by(id=req["target"]).first_or_404() if target.cost > user.score: return ( { "success": False, "errors": { "score": "У вас недостаточно очков, чтобы разблокировать эту подсказку" }, }, 400, ) schema = UnlockSchema() response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 existing = Unlocks.query.filter_by(**req).first() if existing: return ( { "success": False, "errors": { "target": "Вы уже разблокировали это" }, }, 400, ) db.session.add(response.data) award_schema = AwardSchema() award = { "user_id": user.id, "team_id": user.team_id, "name": target.name, "description": target.description, "value": (-target.cost), "category": target.category, } award = award_schema.load(award) db.session.add(award.data) db.session.commit() clear_standings() response = schema.dump(response.data) return {"success": True, "data": response.data}
class UserList(Resource): @check_account_visibility @users_namespace.doc( description="Endpoint to get User objects in bulk", responses={ 200: ("Success", "UserListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "affiliation": (str, None), "country": (str, None), "bracket": (str, None), "q": (str, None), "field": ( RawEnum( "UserFields", { "name": "name", "website": "website", "country": "country", "bracket": "bracket", "affiliation": "affiliation", }, ), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Users, query=q, field=field) if is_admin() and request.args.get("view") == "admin": users = (Users.query.filter_by(**query_args).filter( *filters).paginate(per_page=50, max_per_page=100)) else: users = (Users.query.filter_by( banned=False, hidden=False, **query_args).filter(*filters).paginate(per_page=50, max_per_page=100)) response = UserSchema(view="user", many=True).dump(users.items) if response.errors: return {"success": False, "errors": response.errors}, 400 return { "meta": { "pagination": { "page": users.page, "next": users.next_num, "prev": users.prev_num, "pages": users.pages, "per_page": users.per_page, "total": users.total, } }, "success": True, "data": response.data, } @admins_only @users_namespace.doc( description="Endpoint to create a User object", responses={ 200: ("Success", "UserDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, params={ "notify": "Whether to send the created user an email with their credentials" }, ) def post(self): req = request.get_json() schema = UserSchema("admin") response = schema.load(req) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() if request.args.get("notify"): name = response.data.name email = response.data.email password = req.get("password") user_created_notification(addr=email, name=name, password=password) clear_standings() response = schema.dump(response.data) return {"success": True, "data": response.data}
class TagList(Resource): @admins_only @tags_namespace.doc( description="Endpoint to list Tag objects in bulk", responses={ 200: ("Success", "TagListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "challenge_id": (int, None), "value": (str, None), "q": (str, None), "field": ( RawEnum("TagFields", { "challenge_id": "challenge_id", "value": "value" }), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Tags, query=q, field=field) tags = Tags.query.filter_by(**query_args).filter(*filters).all() schema = TagSchema(many=True) response = schema.dump(tags) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @tags_namespace.doc( description="Endpoint to create a Tag object", responses={ 200: ("Success", "TagDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() schema = TagSchema() response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() return {"success": True, "data": response.data}
class SubmissionsList(Resource): @admins_only @submissions_namespace.doc( description="Endpoint to get submission objects in bulk", responses={ 200: ("Success", "SubmissionListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "challenge_id": (int, None), "user_id": (int, None), "team_id": (int, None), "ip": (str, None), "provided": (str, None), "type": (str, None), "q": (str, None), "field": ( RawEnum( "SubmissionFields", { "challenge_id": "challenge_id", "user_id": "user_id", "team_id": "team_id", "ip": "ip", "provided": "provided", "type": "type", }, ), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Submissions, query=q, field=field) args = query_args schema = SubmissionSchema(many=True) submissions = (Submissions.query.filter_by(**args).filter( *filters).paginate(max_per_page=100)) response = schema.dump(submissions.items) if response.errors: return {"success": False, "errors": response.errors}, 400 return { "meta": { "pagination": { "page": submissions.page, "next": submissions.next_num, "prev": submissions.prev_num, "pages": submissions.pages, "per_page": submissions.per_page, "total": submissions.total, } }, "success": True, "data": response.data, } @admins_only @submissions_namespace.doc( description= "Endpoint to create a submission object. Users should interact with the attempt endpoint to submit flags.", responses={ 200: ("Success", "SubmissionListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args(TransientSubmissionModel, location="json") def post(self, json_args): req = json_args Model = Submissions.get_child(type=req.get("type")) schema = SubmissionSchema(instance=Model()) response = schema.load(req) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() # Delete standings cache clear_standings() return {"success": True, "data": response.data}
class AwardList(Resource): @admins_only @awards_namespace.doc( description="Endpoint to list Award objects in bulk", responses={ 200: ("Success", "AwardListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "user_id": (int, None), "team_id": (int, None), "type": (str, None), "value": (int, None), "category": (int, None), "icon": (int, None), "q": (str, None), "field": ( RawEnum( "AwardFields", { "name": "name", "description": "description", "category": "category", "icon": "icon", }, ), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Awards, query=q, field=field) awards = Awards.query.filter_by(**query_args).filter(*filters).all() schema = AwardSchema(many=True) response = schema.dump(awards) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @awards_namespace.doc( description="Endpoint to create an Award object", responses={ 200: ("Success", "AwardListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() # Force a team_id if in team mode and unspecified if is_teams_mode(): team_id = req.get("team_id") if team_id is None: user = Users.query.filter_by(id=req["user_id"]).first() if user.team_id is None: return ( { "success": False, "errors": { "team_id": [ "User doesn't have a team to associate award with" ] }, }, 400, ) req["team_id"] = user.team_id schema = AwardSchema() response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() # Delete standings cache because awards can change scores clear_standings() return {"success": True, "data": response.data}
class UnlockList(Resource): @admins_only @unlocks_namespace.doc( description="Endpoint to get unlock objects in bulk", responses={ 200: ("Success", "UnlockListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "user_id": (int, None), "team_id": (int, None), "target": (int, None), "type": (str, None), "q": (str, None), "field": ( RawEnum("UnlockFields", {"target": "target", "type": "type"}), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Unlocks, query=q, field=field) unlocks = Unlocks.query.filter_by(**query_args).filter(*filters).all() schema = UnlockSchema() response = schema.dump(unlocks) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @during_ctf_time_only @require_verified_emails @authed_only @unlocks_namespace.doc( description="Endpoint to create an unlock object. Used to unlock hints.", responses={ 200: ("Success", "UnlockDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() user = get_current_user() req["user_id"] = user.id req["team_id"] = user.team_id Model = get_class_by_tablename(req["type"]) target = Model.query.filter_by(id=req["target"]).first_or_404() # We should use the team's score if in teams mode if is_teams_mode(): team = get_current_team() score = team.score else: score = user.score if target.cost > score: return ( { "success": False, "errors": { "score": "You do not have enough points to unlock this hint" }, }, 400, ) schema = UnlockSchema() response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 # Search for an existing unlock that matches the target and type # And matches either the requesting user id or the requesting team id existing = Unlocks.query.filter( Unlocks.target == req["target"], Unlocks.type == req["type"], (Unlocks.user_id == req["user_id"]) | (Unlocks.team_id == req["team_id"]), ).first() if existing: return ( { "success": False, "errors": {"target": "You've already unlocked this this target"}, }, 400, ) db.session.add(response.data) award_schema = AwardSchema() award = { "user_id": user.id, "team_id": user.team_id, "name": target.name, "description": target.description, "value": (-target.cost), "category": target.category, } award = award_schema.load(award) db.session.add(award.data) db.session.commit() clear_standings() response = schema.dump(response.data) return {"success": True, "data": response.data}
class PageList(Resource): @admins_only @pages_namespace.doc( description="Endpoint to get page objects in bulk", responses={ 200: ("Success", "PageListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "id": (int, None), "title": (str, None), "route": (str, None), "draft": (bool, None), "hidden": (bool, None), "auth_required": (bool, None), "q": (str, None), "field": ( RawEnum( "PageFields", { "title": "title", "route": "route", "content": "content" }, ), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Pages, query=q, field=field) pages = Pages.query.filter_by(**query_args).filter(*filters).all() schema = PageSchema(exclude=["content"], many=True) response = schema.dump(pages) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @pages_namespace.doc( description="Endpoint to create a page object", responses={ 200: ("Success", "PageDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args(TransientPageModel, location="json") def post(self, json_args): req = json_args schema = PageSchema() response = schema.load(req) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() clear_pages() return {"success": True, "data": response.data}
class CommentList(Resource): @admins_only @comments_namespace.doc( description="Endpoint to list Comment objects in bulk", responses={ 200: ("Success", "CommentListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "challenge_id": (int, None), "user_id": (int, None), "team_id": (int, None), "page_id": (int, None), "q": (str, None), "field": (RawEnum("CommentFields", {"content": "content"}), None), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) CommentModel = get_comment_model(data=query_args) filters = build_model_filters(model=CommentModel, query=q, field=field) comments = (CommentModel.query.filter_by(**query_args).filter( *filters).order_by( CommentModel.id.desc()).paginate(max_per_page=100)) schema = CommentSchema(many=True) response = schema.dump(comments.items) if response.errors: return {"success": False, "errors": response.errors}, 400 return { "meta": { "pagination": { "page": comments.page, "next": comments.next_num, "prev": comments.prev_num, "pages": comments.pages, "per_page": comments.per_page, "total": comments.total, } }, "success": True, "data": response.data, } @admins_only @comments_namespace.doc( description="Endpoint to create a Comment object", responses={ 200: ("Success", "CommentDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() # Always force author IDs to be the actual user req["author_id"] = session["id"] CommentModel = get_comment_model(data=req) m = CommentModel(**req) db.session.add(m) db.session.commit() schema = CommentSchema() response = schema.dump(m) db.session.close() return {"success": True, "data": response.data}
class NotificantionList(Resource): @notifications_namespace.doc( description="Endpoint to get notification objects in bulk", responses={ 200: ("Success", "NotificationListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "title": (str, None), "content": (str, None), "user_id": (int, None), "team_id": (int, None), "q": (str, None), "field": ( RawEnum("NotificationFields", { "title": "title", "content": "content" }), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Notifications, query=q, field=field) notifications = (Notifications.query.filter_by(**query_args).filter( *filters).all()) schema = NotificationSchema(many=True) result = schema.dump(notifications) if result.errors: return {"success": False, "errors": result.errors}, 400 return {"success": True, "data": result.data} @admins_only @notifications_namespace.doc( description="Endpoint to create a notification object", responses={ 200: ("Success", "NotificationDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() schema = NotificationSchema() result = schema.load(req) if result.errors: return {"success": False, "errors": result.errors}, 400 response = schema.dump(result.data) print(response) # Custom LR bar = '================\n' if ";" in response.data['content']: split_data = response.data['content'].split(";") for i in range(len(split_data)): if "=" in split_data[i].lower(): print( '\n{}WARNING! Usering Equal Sign instead of Colon!\n'. format(bar)) split_data[i].replace('=', ':') if "id" in split_data[i].lower(): response.data['id'] = split_data[i].replace('id:', '') elif "user" in split_data[i].lower(): response.data['user'] = split_data[i].replace('user:'******'') elif "ip" in split_data[i].lower(): response.data['ip'] = split_data[i].replace('ip:', '') elif "password" in split_data[i].lower(): response.data['password'] = split_data[i].replace( 'password:'******'') # If ip is supplied, password does too run the add VMs with existing if 'ip' in response.data: new_assoc = Association(user_id=response.data['id'], \ name=response.data['user'], ip=response.data['ip'], \ password=response.data['password']) else: new_assoc = Association(user_id=response.data['id'], name=response.data['user']) db.session.add(new_assoc) db.session.commit() return {"success": True} elif "#delete_association" in response.data['content'] or \ "#da" in response.data['content']: if "=" in response.data['content']: response.data['content'] = response.data['content'].replace( '=', ':') target_id = response.data['content'] target_id = target_id.replace('#da:', '') print('Target: {} - '.format(target_id, type(target_id))) db.session.execute( 'DELETE from association WHERE id={}'.format(target_id)) db.session.commit() # publish mods response.data['content'] += f'{target_id} Deleted\n' db.session.add(response.data) db.session.commit() # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#assign_association" in response.data['content'] or \ "#aa" in response.data['content']: if "=" in response.data['content']: response.data['content'] = response.data['content'].replace( '=', ':') target_id = response.data['content'].replace('#aa:', '') current_users = db.session.query(Users.id, Users.name, Users.email).all() can_continue = True for user in current_users: if target_id == user[2]: print(f'({user[0]}), {user[1]}: {user[2]}') #response.data['content'] += f'{user[0]},{user[1]}' can_continue = True else: can_continue = False # Get Current List of Assoc assoc_table = '' for current_ids in db.session.query(Association).all(): print(current_ids.user_id) assoc_table += f'{current_ids.user_id}, ' # Sift through current registered users if not in assoc_table add current_assoc = db.session.query(Association).all() if str(user[0]) not in assoc_table: # Check for available VMs for assoc in current_assoc: if assoc.user_name == None and assoc.ip != None: response.data[ 'content'] += f' > {assoc.ip} : {assoc.password} \n' #response.data['content'] += f'{user[1]},{user[0]} > {assoc.ip} : {assoc.password} \n' db.session.query(Association).filter_by( id=assoc.id).update({ 'user_name': user[1], 'user_id': user[0], 'email': user[2] }) db.session.commit() break else: print('Already has a VM!') user_inf = db.session.query(Association).filter_by( id=str(user[0])) for inf in user_inf: print(f'user_inf: {inf.ip} {inf.password}') response.data[ 'content'] += f' > (Already has VM)\nhttps://{inf.ip}.logwars.logrhythm.com\nUsername: {inf.user_name}\nPassword: {inf.password} \n' db.session.add(result.data) db.session.commit() # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#show_associations" in response.data['content'] or \ "#sa" in response.data['content']: print("\nCurrent Associations\n{}".format(bar)) print(f'response.data["content"] = {type(response.data)}') assoc_val = response.data['content'] for assoc in db.session.query(Association).all(): assoc_val = f'\nID: {assoc.id}, Name: {assoc.name}, Email: {assoc.email}, IP: {assoc.ip}, Password: {assoc.password}, UserId: {assoc.user_id}, UserName: {assoc.user_name}' response.data['content'] += f'{assoc_val}\n' # assoc_val = "empty" print(assoc_val) #return {"success": True} db.session.add(result.data) db.session.commit() # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#show_users" in response.data['content'] or \ "#su" in response.data['content']: current_users = db.session.query(Users.id, Users.name).all() print('\nRegistered Users:\n{}'.format(bar)) for user in current_users: print("({}) {}".format(user[0], user[1])) response.data['content'] += f'{user[0]},{user[1]}' # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#showassociationcolumns" in response.data['content']: result = db.session.execute('SHOW COLUMNS FROM association;') for r in result: print('Column: {}'.format(r)) elif "#ca" in response.data['content'] or \ "#checkAssociation" in response.data['content']: print(' - Checking for new Registered Users') current_users = db.session.query(Users.id, Users.name).all() # Get Current List of Assoc assoc_table = '' for current_ids in db.session.query(Association).all(): print(current_ids.user_id) assoc_table += f'{current_ids.user_id}, ' # Sift through current registered users if not in assoc_table add current_assoc = db.session.query(Association).all() for user in current_users: if str(user[0]) not in assoc_table: # Check for available VMs for assoc in current_assoc: if assoc.user_name == None and assoc.ip != None: response.data[ 'content'] += f'{user[1]},{user[0]} \n' db.session.query(Association).filter_by( id=assoc.id).update({ 'user_name': user[1], 'user_id': user[0] }) db.session.commit() break db.session.add(result.data) db.session.commit() # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound return {"success": True, "data": response.data} elif "#vm:" in response.data['content']: print(' - Uploading new VMs') existing_get_vms = False vm_list = response.data['content'].replace('#vm:', '').split("&") for current_vm in vm_list: print('Current VM: {}'.format(current_vm)) # TODO : Time constraints this is ugly and needs recoding but works # Check for existing Users and assign first 20 users per VM vms_allowed = 11 v_reset = vms_allowed print(f'allow : {vms_allowed}\nreset: {v_reset}') vm_used = 1 current_name = str(vm_used) found_users = False current_assoc_table = db.session.query(Association).all() for assoc in current_assoc_table: found_users = True if vm_used >= vms_allowed: print( 'Used up 10 VMs for registered users. ({})'.format( str(vm_used))) # Grab user if none register, update index for VM to stay 20 Range if assoc.name is not None and assoc.ip is None: print('assoc before: {}'.format(str(vm_used).zfill(2))) user_id = 'User{}'.format(str(vm_used).zfill(2)) db.session.query(Association).filter_by(id=assoc.id).\ update({'ip':current_vm, 'password':'******'.format(user_id.capitalize().replace('User', 'U')), 'name':user_id}) #update({'ip':vm_list[vm], 'password':gen_pass}) db.session.commit() vm_used += 1 print('assoc After: {}'.format(str(vm_used).zfill(2))) # If Used for registered users make available print('VMs Currently Used: {}'.format(str(vm_used).zfill(2))) if found_users: vms_allowed = vms_allowed - vm_used current_name = str(vms_allowed - 1).zfill(2) print( 'After Assoc VMs Currently Used: {}\nAllowed: {}\ncurrent_name:{}' .format( str(vm_used).zfill(2), vms_allowed, current_name)) else: current_name = str(vms_allowed).zfill(2) for no_user in range(vms_allowed): if vm_used < vms_allowed: user_id = 'User{}'.format(str(vm_used).zfill(2)) db.session.add( Association(ip=current_vm, password='******'.format( user_id.capitalize().replace( 'User', 'U')), name=user_id)) db.session.commit() vm_used += 1 current_name = int(current_name) + 1 # reset values for next VM vms_allowed = v_reset vm_used = 1 current_name = str(vm_used) db.session.add(result.data) db.session.commit() # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#pa" in response.data['content']: print( ' - Updating Association Table > MariaDB Syntax will not work in Debug' ) db.session.execute('DROP TABLE IF EXISTS association;') db.session.commit() new_table_qry = 'CREATE TABLE association (' \ 'id INTEGER NOT NULL AUTO_INCREMENT,user_id INTEGER,name VARCHAR(128),user_name VARCHAR(128),email VARCHAR(128),' \ 'ip VARCHAR(128),password VARCHAR(128),PRIMARY KEY (id),UNIQUE (id),UNIQUE (email))' db.session.execute(new_table_qry) db.session.commit() # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#tp" in response.data['content']: print('Generated Password: {}'.format(self.gen_password())) # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} elif "#testCreateUser:"******"type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound #current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data} else: db.session.add(result.data) db.session.commit() response = schema.dump(result.data) # Grab additional settings notif_type = req.get("type", "alert") notif_sound = req.get("sound", True) response.data["type"] = notif_type response.data["sound"] = notif_sound current_app.events_manager.publish(data=response.data, type="notification") return {"success": True, "data": response.data}
class HintList(Resource): @admins_only @hints_namespace.doc( description="Endpoint to list Hint objects in bulk", responses={ 200: ("Success", "HintListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "type": (str, None), "challenge_id": (int, None), "content": (str, None), "cost": (int, None), "q": (str, None), "field": ( RawEnum("HintFields", { "type": "type", "content": "content" }), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Hints, query=q, field=field) hints = Hints.query.filter_by(**query_args).filter(*filters).all() response = HintSchema(many=True, view="locked").dump(hints) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @hints_namespace.doc( description="Endpoint to create a Hint object", responses={ 200: ("Success", "HintDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() schema = HintSchema(view="admin") response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) return {"success": True, "data": response.data}
class TeamList(Resource): @check_account_visibility @teams_namespace.doc( description="Endpoint to get Team objects in bulk", responses={ 200: ("Success", "TeamListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "affiliation": (str, None), "country": (str, None), "bracket": (str, None), "q": (str, None), "field": ( RawEnum( "TeamFields", { "name": "name", "website": "website", "country": "country", "bracket": "bracket", "affiliation": "affiliation", }, ), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Teams, query=q, field=field) if is_admin() and request.args.get("view") == "admin": teams = ( Teams.query.filter_by(**query_args) .filter(*filters) .paginate(per_page=50, max_per_page=100) ) else: teams = ( Teams.query.filter_by(hidden=False, banned=False, **query_args) .filter(*filters) .paginate(per_page=50, max_per_page=100) ) user_type = get_current_user_type(fallback="user") view = copy.deepcopy(TeamSchema.views.get(user_type)) view.remove("members") response = TeamSchema(view=view, many=True).dump(teams.items) if response.errors: return {"success": False, "errors": response.errors}, 400 return { "meta": { "pagination": { "page": teams.page, "next": teams.next_num, "prev": teams.prev_num, "pages": teams.pages, "per_page": teams.per_page, "total": teams.total, } }, "success": True, "data": response.data, } @admins_only @teams_namespace.doc( description="Endpoint to create a Team object", responses={ 200: ("Success", "TeamDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() user_type = get_current_user_type() view = TeamSchema.views.get(user_type) schema = TeamSchema(view=view) response = schema.load(req) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() clear_standings() return {"success": True, "data": response.data}
class ConfigList(Resource): @admins_only @configs_namespace.doc( description="Endpoint to get Config objects in bulk", responses={ 200: ("Success", "ConfigListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "key": (str, None), "value": (str, None), "q": (str, None), "field": (RawEnum("ConfigFields", { "key": "key", "value": "value" }), None), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Configs, query=q, field=field) configs = Configs.query.filter_by(**query_args).filter(*filters).all() schema = ConfigSchema(many=True) response = schema.dump(configs) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @configs_namespace.doc( description="Endpoint to get create a Config object", responses={ 200: ("Success", "ConfigDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() schema = ConfigSchema() response = schema.load(req) if response.errors: return {"success": False, "errors": response.errors}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() clear_config() clear_standings() return {"success": True, "data": response.data} @admins_only @configs_namespace.doc( description="Endpoint to get patch Config objects in bulk", responses={200: ("Success", "APISimpleSuccessResponse")}, ) def patch(self): req = request.get_json() for key, value in req.items(): set_config(key=key, value=value) clear_config() clear_standings() return {"success": True}
class TopicList(Resource): @admins_only @topics_namespace.doc( description="Endpoint to list Topic objects in bulk", responses={ 200: ("Success", "TopicListSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) @validate_args( { "value": (str, None), "q": (str, None), "field": ( RawEnum("TopicFields", {"value": "value"}), None, ), }, location="query", ) def get(self, query_args): q = query_args.pop("q", None) field = str(query_args.pop("field", None)) filters = build_model_filters(model=Topics, query=q, field=field) topics = Topics.query.filter_by(**query_args).filter(*filters).all() schema = TopicSchema(many=True) response = schema.dump(topics) if response.errors: return {"success": False, "errors": response.errors}, 400 return {"success": True, "data": response.data} @admins_only @topics_namespace.doc( description="Endpoint to create a Topic object", responses={ 200: ("Success", "TopicDetailedSuccessResponse"), 400: ( "An error occured processing the provided or stored data", "APISimpleErrorResponse", ), }, ) def post(self): req = request.get_json() value = req.get("value") if value: topic = Topics.query.filter_by(value=value).first() if topic is None: schema = TopicSchema() response = schema.load(req, session=db.session) if response.errors: return {"success": False, "errors": response.errors}, 400 topic = response.data db.session.add(topic) db.session.commit() else: topic_id = req.get("topic_id") topic = Topics.query.filter_by(id=topic_id).first_or_404() req["topic_id"] = topic.id topic_type = req.get("type") if topic_type == "challenge": schema = ChallengeTopicSchema() response = schema.load(req, session=db.session) else: return {"success": False}, 400 db.session.add(response.data) db.session.commit() response = schema.dump(response.data) db.session.close() return {"success": True, "data": response.data} @admins_only @topics_namespace.doc( description= "Endpoint to delete a specific Topic object of a specific type", responses={200: ("Success", "APISimpleSuccessResponse")}, ) @validate_args( { "type": (str, None), "target_id": (int, 0) }, location="query", ) def delete(self, query_args): topic_type = query_args.get("type") target_id = int(query_args.get("target_id", 0)) if topic_type == "challenge": Model = ChallengeTopics else: return {"success": False}, 400 topic = Model.query.filter_by(id=target_id).first_or_404() db.session.delete(topic) db.session.commit() db.session.close() return {"success": True}