def upsert_bundle(bundle): """ Add or update a bundle. Args: bundle: bundle dict Returns: The created/updated bundle ID. """ # Validate the bundle object validate(bundle_schema, bundle) db = api.db.get_conn() bid = api.common.hash("{}-{}".format(bundle["name"], bundle["author"])) # If the bundle already exists, update it instead existing = db.bundles.find_one({'bid': bid}, {'_id': 0}) if existing is not None: db.bundles.find_one_and_update( {'bid': bid}, {'$set': bundle} ) return bid bundle["bid"] = bid bundle["dependencies_enabled"] = False db.bundles.insert(bundle) return bid
def upsert_problem(problem, sid): """ Add or update a problem. Args: problem: problem dict sid: shell server ID Returns: The created/updated problem ID. """ db = api.db.get_conn() # Validate the problem object # @TODO it may make more sense to do this with e.g. Marshmallow at the # routing level validate(problem_schema, problem) for instance in problem["instances"]: validate(instance_schema, instance) problem["pid"] = api.common.hash("{}-{}".format(problem["name"], problem["author"])) # Initially disable problems problem["disabled"] = True # Assign instance IDs and server numbers server_number = api.shell_servers.get_server(sid)['server_number'] for instance in problem["instances"]: instance["iid"] = api.common.hash( str(instance["instance_number"]) + sid + problem["pid"]) instance["sid"] = sid if server_number is not None: instance["server_number"] = server_number if problem.get("walkthrough"): # Falsy for None and empty string problem["has_walkthrough"] = True else: problem["has_walkthrough"] = False # If the problem already exists, update it instead existing = db.problems.find_one({'pid': problem['pid']}, {'_id': 0}) if existing is not None: # Copy over instances on other shell servers from the existing version other_server_instances = [ i for i in existing['instances'] if i['sid'] != sid ] problem['instances'].extend(other_server_instances) # Copy over the disabled state from the old problem, or # set to true if there are no instances problem["disabled"] = (existing["disabled"] or len(problem["instances"]) == 0) db.problems.find_one_and_update({'pid': problem['pid']}, {'$set': problem}) return problem['pid'] db.problems.insert(problem) return problem['pid']
def upsert_feedback(pid, feedback): """ Add or update problem feedback in the database. Args: pid: the problem id feedback: the problem feedback. Raises: PicoException if provided pid does not exist """ db = api.db.get_conn() uid = api.user.get_user()['uid'] # Make sure the problem actually exists. if not api.problem.get_problem(pid, {"pid": 1}): raise PicoException('Problem not found', 404) team = api.user.get_team(uid=uid) solved = pid in api.problem.get_solved_pids(tid=team["tid"]) validate(feedback_schema, feedback) # update feedback if already present if get_problem_feedback(pid=pid, uid=uid) != []: db.problem_feedback.update({ "pid": pid, "uid": uid }, {"$set": { "timestamp": datetime.utcnow(), "feedback": feedback }}) else: db.problem_feedback.insert({ "pid": pid, "uid": uid, "tid": team["tid"], "solved": solved, "timestamp": datetime.utcnow(), "feedback": feedback })
def change_group_settings(gid, settings): """ Replace the current settings with the supplied ones. Args: gid: ID of the group to update settings: new settings object Raises: PicoException if attempting to change 'hidden' from true to false """ db = api.db.get_conn() validate(group_settings_schema, settings) group = api.group.get_group(gid=gid) if group["settings"]["hidden"] and not settings["hidden"]: raise PicoException("Cannot make a previously hidden group public", status_code=422) db.groups.update({"gid": group["gid"]}, {"$set": {"settings": settings}})
async def message_handler(self, text, message_id): if not api.validate(text): logging.debug('invalid url received: {0}'.format(text)) await self.reply(replies.INVALID_URL, message_id) return await self.reply(replies.UPLOAD_STARTED, message_id) video_data = api.download(text, config['tmp_path']) video_vk_url = api.upload_video(vk_api, video_data.path, video_data.name) os.remove(video_data.path) logging.info("uploaded: {0} -> {1}".format(text, video_vk_url)) await self.reply(replies.UPLOADED_VIDEO.format(video_vk_url), message_id)
def submit_key(tid, pid, key, method, uid, ip=None): """ User problem submission. Args: tid: user's team id pid: problem's pid key: answer text method: submission method (e.g. 'game') uid: user's uid ip: user's ip Returns: tuple: (correct, previously_solved_by_user, previously_solved_by_team) """ db = api.db.get_conn() validate(submission_schema, {"tid": tid, "pid": pid, "key": key}) if pid not in api.problem.get_unlocked_pids(tid): raise PicoException( "You can't submit flags to problems you haven't unlocked.", 422) previously_solved_by_user = db.submissions.find_one(filter={ 'pid': pid, 'uid': uid, 'correct': True }) is not None previously_solved_by_team = db.submissions.find_one(filter={ 'pid': pid, 'tid': tid, 'correct': True }) is not None correct = grade_problem(pid, key, tid) if not previously_solved_by_user: db.submissions.insert({ 'uid': uid, 'tid': tid, 'timestamp': datetime.utcnow(), 'pid': pid, 'ip': ip, 'key': key, 'method': method, 'category': api.problem.get_problem(pid)['category'], 'correct': correct, }) if correct and not previously_solved_by_team: # Immediately invalidate some caches cache.invalidate(api.stats.get_score, tid) cache.invalidate(api.stats.get_score, uid) cache.invalidate(api.problem.get_unlocked_pids, tid) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=uid, category=None) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=None, category=None) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=uid) cache.invalidate(api.problem.get_solved_problems, tid=tid) cache.invalidate(api.problem.get_solved_problems, uid=uid) cache.invalidate(api.stats.get_score_progression, tid=tid, category=None) cache.invalidate(api.stats.get_score_progression, tid=tid) # @TODO achievement processing needs to be fixed/reviewed # api.achievement.process_achievements("submit", { # "uid": uid, # "tid": tid, # "pid": pid # }) return (correct, previously_solved_by_user, previously_solved_by_team)
def submit_key(tid, pid, key, method, uid, ip=None): """ User problem submission. Args: tid: user's team id pid: problem's pid key: answer text method: submission method (e.g. 'game') uid: user's uid ip: user's ip Returns: tuple: (correct, previously_solved_by_user, previously_solved_by_team) """ db = api.db.get_conn() validate(submission_schema, {"tid": tid, "pid": pid, "key": key}) if pid not in api.problem.get_unlocked_pids(tid): raise PicoException( "You can't submit flags to problems you haven't unlocked.", 422) previously_solved_by_user = (db.submissions.find_one(filter={ "pid": pid, "uid": uid, "correct": True }) is not None) previously_solved_by_team = (db.submissions.find_one(filter={ "pid": pid, "tid": tid, "correct": True }) is not None) correct, suspicious = grade_problem(pid, key, tid) if not previously_solved_by_user: db.submissions.insert({ "uid": uid, "tid": tid, "timestamp": datetime.utcnow(), "pid": pid, "ip": ip, "key": key, "method": method, "category": api.problem.get_problem(pid, {"category": 1})["category"], "correct": correct, "suspicious": suspicious, }) if correct and not previously_solved_by_team: # Immediately invalidate some caches cache.invalidate(api.stats.get_score, tid) cache.invalidate(api.stats.get_score, uid) cache.invalidate(api.problem.get_unlocked_pids, tid) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=uid, category=None) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=None, category=None) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=uid) cache.invalidate(api.problem.get_solved_problems, tid=tid) cache.invalidate(api.problem.get_solved_problems, uid=uid) cache.invalidate(api.stats.get_score_progression, tid=tid, category=None) cache.invalidate(api.stats.get_score_progression, tid=tid) if suspicious: cache.invalidate(api.submissions.get_suspicious_submissions, tid) return (correct, previously_solved_by_user, previously_solved_by_team)
def submit_key(tid, pid, key, method, uid, ip=None): """ User problem submission. Args: tid: user's team id pid: problem's pid key: answer text method: submission method (e.g. 'game') uid: user's uid ip: user's ip Returns: tuple: (correct, previously_solved_by_user, previously_solved_by_team) """ db = api.db.get_conn() validate(submission_schema, {"tid": tid, "pid": pid, "key": key}) if pid not in api.problem.get_unlocked_pids(tid): raise PicoException( "You can't submit flags to problems you haven't unlocked.", 422 ) previously_solved_by_user = ( db.submissions.find_one(filter={"pid": pid, "uid": uid, "correct": True}) is not None ) previously_solved_by_team = ( db.submissions.find_one(filter={"pid": pid, "tid": tid, "correct": True}) is not None ) correct, suspicious = grade_problem(pid, key, tid) if not previously_solved_by_user: count_solves = api.stats.get_problem_solves(pid) bonus = 10 - count_solves db.submissions.insert( { "uid": uid, "tid": tid, "timestamp": datetime.utcnow(), "pid": pid, "ip": ip, "key": key, "method": method, "category": api.problem.get_problem(pid, {"category": 1})["category"], "correct": correct, "suspicious": suspicious, "bonus": bonus, } ) if correct and not previously_solved_by_team: # Immediately invalidate some caches cache.invalidate(api.stats.get_score, tid) cache.invalidate(api.stats.get_score, uid) cache.invalidate(api.problem.get_unlocked_pids, tid) cache.invalidate( api.problem.get_solved_problems, tid=tid, uid=uid, category=None ) cache.invalidate( api.problem.get_solved_problems, tid=tid, uid=None, category=None ) cache.invalidate(api.problem.get_solved_problems, tid=tid, uid=uid) cache.invalidate(api.problem.get_solved_problems, tid=tid) cache.invalidate(api.problem.get_solved_problems, uid=uid) cache.invalidate(api.stats.get_score_progression, tid=tid, category=None) cache.invalidate(api.stats.get_score_progression, tid=tid) # if the solve is correct there is no need to maintain the container if correct: instance = api.problem.get_instance_data(pid, tid) if "docker_challenge" in instance and instance["docker_challenge"]: containers = api.docker.submission_to_cid(tid, pid) for c in containers: cid = c["cid"] api.docker.delete(cid) if suspicious: cache.invalidate(api.submissions.get_suspicious_submissions, tid) return (correct, previously_solved_by_user, previously_solved_by_team)
def upsert_problem(problem, sid): """ Add or update a problem. Args: problem: problem dict sid: shell server ID Returns: The created/updated problem ID. """ db = api.db.get_conn() # Validate the problem object # @TODO it may make more sense to do this with e.g. Marshmallow at the # routing level validate(problem_schema, problem) for instance in problem["instances"]: validate(instance_schema, instance) problem["pid"] = problem["unique_name"] # Initially disable problems problem["disabled"] = True # Assign instance IDs and server numbers server_number = api.shell_servers.get_server(sid)["server_number"] for instance in problem["instances"]: instance["iid"] = api.common.hash( str(instance["instance_number"]) + sid + problem["pid"] ) instance["sid"] = sid if server_number is not None: instance["server_number"] = server_number # Docker Instance tracking # XXX: also track port information and TTL digests = [] for i in problem["instances"]: if "docker_challenge" in i and i["docker_challenge"]: digests.append(i["instance_digest"]) try: docker_pub = current_app.config["DOCKER_PUB"] except KeyError: raise PicoException("Attempted to load a DockerChallenge but DOCKER_PUB not configured") # update port display style with docker host value for p, v in i["port_info"].items(): v["fmt"] = v["fmt"].format(host=docker_pub) # track problem to image information for docker instances pid = problem["pid"] if len(digests) > 0: data = {"pid": pid, "digests": digests} db.images.update({"pid": pid}, data, upsert=True) if problem.get("walkthrough"): # Falsy for None and empty string problem["has_walkthrough"] = True else: problem["has_walkthrough"] = False # If the problem already exists, update it instead existing = db.problems.find_one({"pid": problem["pid"]}, {"_id": 0}) if existing is not None: # Copy over instances on other shell servers from the existing version other_server_instances = [i for i in existing["instances"] if i["sid"] != sid] problem["instances"].extend(other_server_instances) # Copy over the disabled state from the old problem, or # set to true if there are no instances problem["disabled"] = existing["disabled"] or len(problem["instances"]) == 0 db.problems.find_one_and_update({"pid": problem["pid"]}, {"$set": problem}) return problem["pid"] db.problems.insert(problem) return problem["pid"]