def update_achievement(aid, updated_achievement): """ Updates a achievement with new properties. Args: aid: the aid of the achievement to update. updated_achievement: an updated achievement object. Returns: The updated achievement object. """ db = api.common.get_conn() if updated_achievement.get("name", None) is not None: if safe_fail(get_achievement, name=updated_achievement["name"]) is not None: raise WebException("Achievement with identical name already exists.") achievement = get_achievement(aid=aid, show_disabled=True).copy() achievement.update(updated_achievement) # pass validation by removing/readding aid achievement.pop("aid") validate(achievement_schema, achievement) achievement["aid"] = aid db.achievements.update({"aid": aid}, achievement) api.cache.fast_cache.clear() return achievement
def delete_group_hook(): """ Tries to delete a group. Validates forms. All required arguments are assumed to be keys in params. """ params = api.common.flat_multi(request.form) validate(delete_group_schema, params) if params.get("group-owner"): owner_team = api.team.get_team(name=params["group-owner"]) else: owner_team = api.team.get_team() group = api.group.get_group( name=params["group-name"], owner_tid=owner_team["tid"]) user = api.user.get_user() roles = api.group.get_roles_in_group(group["gid"], uid=user["uid"]) if roles["owner"]: api.group.delete_group(group["gid"]) else: raise WebException("Only the owner of a classroom can delete it!") return WebSuccess("Successfully deleted classroom")
def join_group_hook(): """ Tries to place a team into a group. Validates forms. All required arguments are assumed to be keys in params. """ params = api.common.flat_multi(request.form) validate(join_group_schema, params) owner_team = api.team.get_team(name=params["group-owner"]) if safe_fail(api.group.get_group, name=params["group-name"], owner_tid=owner_team["tid"]) is None: raise WebException("No class exists with that name!") group = api.group.get_group(name=params["group-name"], owner_tid=owner_team["tid"]) group_settings = api.group.get_group_settings(gid=group["gid"]) team = api.team.get_team() for member_uid in api.team.get_team_uids(tid=team["tid"]): member = api.user.get_user(uid=member_uid) if not api.user.verify_email_in_whitelist(member["email"], group_settings["email_filter"]): raise WebException( "{}'s email does not belong to the whitelist for that group. Your team may not join this group at this time.".format( member["username"] ) ) roles = api.group.get_roles_in_group(group["gid"], tid=team["tid"]) if roles["teacher"] or roles["member"]: raise WebException("Your team is already a member of that class!") api.group.join_group(group["gid"], team["tid"]) return WebSuccess("Successfully joined group")
def update_problem(pid, updated_problem): """ Updates a problem with new properties. Args: pid: the pid of the problem to update. updated_problem: an updated problem object. Returns: The updated problem object. """ db = api.common.get_conn() if updated_problem.get("name", None) is not None: if safe_fail(get_problem, name=updated_problem["name"]) is not None: raise WebException("Problem with identical name already exists.") problem = get_problem(pid=pid, show_disabled=True).copy() problem.update(updated_problem) # pass validation by removing/readding pid problem.pop("pid", None) validate(problem_schema, problem) problem["pid"] = pid db.problems.update({"pid": pid}, problem) api.cache.fast_cache.clear() return problem
def login(username, password): """ Authenticates a user. """ # Read in submitted username and password validate(user_login_schema, { "username": username, "password": password }) user = safe_fail(api.user.get_user, name=username) if user is None: raise WebException("Incorrect username.") if user.get("disabled", False): raise WebException("This account has been disabled.") if confirm_password(password, user['password_hash']): if debug_disable_general_login: if session.get('debugaccount', False): raise WebException("Correct credentials! But the game has not started yet...") if user['uid'] is not None: session['uid'] = user['uid'] session.permanent = True else: raise WebException("Login Error") else: raise WebException("Incorrect password")
def delete_group_hook(): """ Tries to delete a group. Validates forms. All required arguments are assumed to be keys in params. """ params = api.common.flat_multi(request.form) validate(delete_group_schema, params) if params.get("group-owner"): owner_team = api.team.get_team(name=params["group-owner"]) else: owner_team = api.team.get_team() group = api.group.get_group(name=params["group-name"], owner_tid=owner_team["tid"]) user = api.user.get_user() roles = api.group.get_roles_in_group(group["gid"], uid=user["uid"]) if roles["owner"]: api.group.delete_group(group["gid"]) else: raise WebException("Only the owner of a group can delete it!") return WebSuccess("Successfully deleted group")
def add_writeup(pid, uid, title, url): # TODO: import rfc3987 checker and check url validity if uid is None: uid = api.user.get_user()["uid"] db = api.common.get_conn() wid = api.common.token() # Make sure the problem actually exists, and that this team has solved it. api.problem.get_problem(pid=pid) team = api.user.get_team(uid=uid) solved = pid in api.problem.get_solved_pids(tid=team["tid"]) # TODO: fix this assert solved writeup = { "wid": wid, "uid": uid, "pid": pid, "tid": team["tid"], "title": title, "url": url } validate(writeup_schema, writeup) db.writeups.insert(writeup) api.cache.invalidate_memoization(api.problem.get_problem, {"args":pid})
def request_password_reset(username): """ Emails a user a link to reset their password. Checks that a username was submitted to the function and grabs the relevant team info from the db. Generates a secure token and inserts it into the team's document as 'password_reset_token'. A link is emailed to the registered email address with the random token in the url. The user can go to this link to submit a new password, if the token submitted with the new password matches the db token the password is hashed and updated in the db. Args: username: the username of the account """ validate(password_reset_request_schema, {"username":username}) user = safe_fail(api.user.get_user, name=username) if user is None: raise WebException("No registration found for '{}'.".format(username)) token = api.common.token() api.user.set_password_reset_token(user['uid'], token) msgBody = """We recently received a request to reset the password for the following {0} account:\n\n\t{2}\n\nOur records show that this is the email address used to register the above account. If you did not request to reset the password for the above account then you need not take any further steps. If you did request the password reset please follow the link below to set your new password. \n\n {1}/reset#{3} \n\n Best of luck! \n\n ~The {0} Team """.format(api.config.competition_name, api.config.competition_urls[0], username, token) send_email(user['email'], "{} Password Reset".format(api.config.competition_name), msgBody)
def create_new_team_request(params, uid=None): """ Fulfills new team requests for users who have already registered. Args: team_name: The desired name for the team. Must be unique across users and teams. team_password: The team's password. Returns: True if successful, exception thrown otherwise. """ user = api.user.get_user(uid=uid) if user["teacher"]: raise InternalException("Teachers may not create teams!") validate(new_team_schema, params) current_team = api.team.get_team(tid=user["tid"]) if current_team["team_name"] != user["username"]: raise InternalException( "You can only create one new team per user account!") desired_tid = create_team({ "team_name": params["team_name"], "password": api.common.hash_password(params["team_password"]), "affiliation": current_team["affiliation"], "creator": user["uid"], "country": user["country"], }) return join_team(params["team_name"], params["team_password"], user["uid"])
def login(username, password): """ Authenticates a user. """ # Read in submitted username and password validate(user_login_schema, {"username": username, "password": password}) user = safe_fail(api.user.get_user, name=username) if user is None: raise WebException("Incorrect username.") if user.get("disabled", False): raise WebException("This account has been disabled.") if confirm_password(password, user['password_hash']): if debug_disable_general_login: if session.get('debugaccount', False): raise WebException( "Correct credentials! But the game has not started yet...") if user['uid'] is not None: session['uid'] = user['uid'] session.permanent = True else: raise WebException("Login Error") else: raise WebException("Incorrect Password")
def request_password_reset(username): """ Emails a user a link to reset their password. Checks that a username was submitted to the function and grabs the relevant team info from the db. Generates a secure token and inserts it into the team's document as 'password_reset_token'. A link is emailed to the registered email address with the random token in the url. The user can go to this link to submit a new password, if the token submitted with the new password matches the db token the password is hashed and updated in the db. Args: username: the username of the account """ validate(password_reset_request_schema, {"username": username}) user = safe_fail(api.user.get_user, name=username) if user is None: raise WebException("No registration found for '{}'.".format(username)) token = api.common.token() api.user.set_password_reset_token(user['uid'], token) msgBody = """We recently received a request to reset the password for the following {0} account:\n\n\t{2}\n\nOur records show that this is the email address used to register the above account. If you did not request to reset the password for the above account then you need not take any further steps. If you did request the password reset please follow the link below to set your new password. \n\n {1}/reset#{3} \n\n Best of luck! \n\n ~The {0} Team """.format(api.config.competition_name, api.config.competition_urls[0], username, token) send_email(user['email'], "{} Password Reset".format(api.config.competition_name), msgBody)
def add_problem_feedback(pid, uid, feedback): """ Add user problem feedback to the database. Args: pid: the problem id uid: the user id feedback: the problem feedback. """ db = api.common.get_conn() #Make sure the problem actually exists. api.problem.get_problem(pid=pid) 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 }) api.achievement.process_achievements("review", {"uid": uid, "tid": team['tid'], "pid": pid})
def add_server(params): """ Add a shell server to the pool of servers. Args: params: A dict containing: host port username password Returns: The sid. """ db = api.common.get_conn() validate(server_schema, params) if isinstance(params["port"], str): params["port"] = int(params["port"]) params["sid"] = api.common.token() db.shell_servers.insert(params) return params["sid"]
def update_server(sid, params): """ Update a shell server from the pool of servers. Args: sid: The sid of the server to update params: A dict containing: port username password """ db = api.common.get_conn() validate(server_schema, params) server = safe_fail(get_server, sid=sid) if server is None: raise WebException("Shell server with sid '{}' does not exist.".format(sid)) params["name"] = server["name"] validate(server_schema, params) if isinstance(params["port"], str): params["port"] = int(params["port"]) db.shell_servers.update({"sid": server["sid"]}, {"$set": params})
def add_server(params): """ Add a shell server to the pool of servers. Args: params: A dict containing: host port username password Returns: The sid. """ db = api.common.get_conn() validate(server_schema, params) if isinstance(params["port"], str): params["port"] = int(params["port"]) if safe_fail(get_server, name=params["name"]) is not None: raise WebException("Shell server with this name already exists") params["sid"] = api.common.hash(params["name"]) db.shell_servers.insert(params) return params["sid"]
def reset_password(token_value, password, confirm_password): """ Perform the password update operation. Gets a token and new password from a submitted form, if the token is found in a team object in the database the new password is hashed and set, the token is then removed and an appropriate response is returned. Args: token_value: the password reset token password: the password to set confirm_password: the same password again """ validate(password_reset_schema, { "token": token_value, "password": password }) uid = api.token.find_key_by_token("password_reset", token_value)["uid"] api.user.update_password_request( { "new-password": password, "new-password-confirmation": confirm_password }, uid=uid) api.token.delete_token({"uid": uid}, "password_reset")
def create_new_team_request(params, uid=None): """ Fulfills new team requests for users who have already registered. Args: team_name: The desired name for the team. Must be unique across users and teams. team_password: The team's password. Returns: True if successful, exception thrown elsewise. """ validate(new_team_schema, params) user = api.user.get_user(uid=uid) current_team = api.team.get_team(tid=user["tid"]) if current_team["team_name"] != user["username"]: raise InternalException("You can only create one new team per user account!") desired_tid = create_team( { "team_name": params["team_name"], "password": params["team_password"], # The team's affiliation becomes the creator's affiliation. "affiliation": current_team["affiliation"], "eligible": True, } ) return join_team(params["team_name"], params["team_password"], user["uid"])
def join_group_request(params, tid=None): """ Tries to place a team into a group. Validates forms. All required arguments are assumed to be keys in params. Args: group-name: The name of the group to join. group-owner: The name of the owner of the group Optional: tid: If omitted,the tid will be grabbed from the logged in user. """ owner_uid = api.user.get_user(name=params["group-owner"])["uid"] validate(join_group_schema, params) if safe_fail(get_group, name=params["group-name"], owner_uid=owner_uid) is None: raise WebException("No class exists with that name!") group = get_group(name=params["group-name"], owner_uid=owner_uid) if tid is None: tid = api.user.get_team()["tid"] if tid in group['members']: raise WebException("Your team is already a member of that class!") join_group(tid, group["gid"])
def delete_group_request(params, uid=None): """ Tries to delete a group. Validates forms. All required arguments are assumed to be keys in params. Args: group-name: The name of the group to join. Optional: uid: If omitted, the uid will be grabbed from the logged in user. """ validate(delete_group_schema, params) if uid is None: uid = api.user.get_user()['uid'] if safe_fail(get_group, name=params['group-name'], owner_uid=uid) is None: raise WebException("No class exists with that name!") if uid is None: uid = api.user.get_user()["uid"] group = get_group(name=params["group-name"], owner_uid=uid) delete_group(group['gid'])
def insert_problem(problem, sid=None): """ Inserts a problem into the database. Does sane validation. Args: Problem dict. score: points awarded for completing the problem. category: problem's category author: author of the problem description: description of the problem. Optional: version: version of the problem tags: list of problem tags. hints: hints for completing the problem. organization: Organization that author is associated with Returns: The newly created problem id. """ if sid is None: raise InternalException("Must provide an sid to insert problem.") db = api.common.get_conn() validate(problem_schema, problem) # initially disable problems problem["disabled"] = True problem["pid"] = api.common.hash("{}-{}".format(problem["name"], problem["author"])) for instance in problem["instances"]: validate(instance_schema, instance) set_instance_ids(problem, sid) if safe_fail(get_problem, pid=problem["pid"]) is not None: # problem is already inserted, so update instead old_problem = copy(get_problem(pid=problem["pid"])) # leave all instances from different shell server instances = list(filter(lambda i: i["sid"] != sid, old_problem["instances"])) # add instances from this shell server instances.extend(problem["instances"]) problem["instances"] = instances # disable problems with zero instances problem["disabled"] = old_problem["disabled"] or len(problem["instances"]) == 0 # run the update update_problem(problem["pid"], problem) return if safe_fail(get_problem, name=problem["name"]) is not None: raise WebException("Problem with identical name \"{}\" already exists.".format(problem["name"])) db.problems.insert(problem) api.cache.fast_cache.clear() return problem["pid"]
def delete_group_request(params, uid=None): """ Tries to delete a group. Validates forms. All required arguments are assumed to be keys in params. Args: group-name: The name of the group to join. Optional: uid: If omitted, the uid will be grabbed from the logged in user. """ validate(delete_group_schema, params) if uid is None: uid = api.user.get_user()["uid"] if safe_fail(get_group, name=params["group-name"], owner_uid=uid) is None: raise WebException("No class exists with that name!") if uid is None: uid = api.user.get_user()["uid"] group = get_group(name=params["group-name"], owner_uid=uid) delete_group(group["gid"])
def update_server(sid, params): """ Update a shell server from the pool of servers. Args: sid: The sid of the server to update params: A dict containing: port username password """ db = api.common.get_conn() validate(server_schema, params) server = safe_fail(get_server, sid=sid) if server is None: raise WebException( "Shell server with sid '{}' does not exist.".format(sid)) params["name"] = server["name"] validate(server_schema, params) if isinstance(params["port"], str): params["port"] = int(params["port"]) db.shell_servers.update({"sid": server["sid"]}, {"$set": params})
def join_group_request(params, tid=None): """ Tries to place a team into a group. Validates forms. All required arguments are assumed to be keys in params. Args: group-name: The name of the group to join. group-owner: The name of the owner of the group Optional: tid: If omitted,the tid will be grabbed from the logged in user. """ owner_uid = api.user.get_user(name=params["group-owner"])["uid"] validate(join_group_schema, params) if safe_fail(get_group, name=params["group-name"], owner_uid=owner_uid) is None: raise WebException("No class exists with that name!") group = get_group(name=params["group-name"], owner_uid=owner_uid) if tid is None: tid = api.user.get_team()["tid"] if tid in group["members"]: raise WebException("Your team is already a member of that class!") join_group(tid, group["gid"])
def insert_achievement(achievement): """ Inserts an achievement object into the database. Args: achievement: the achievement object loaded from json. Returns: The achievment's aid. """ db = api.common.get_conn() validate(achievement_schema, achievement) achievement["disabled"] = achievement.get("disabled", False) achievement["aid"] = api.common.hash(achievement["name"]) if safe_fail(get_achievement, aid=achievement["aid"]) is not None: raise WebException("achievement with identical aid already exists.") if safe_fail(get_achievement, name=achievement["name"]) is not None: raise WebException("achievement with identical name already exists.") db.achievements.insert(achievement) api.cache.fast_cache.clear() return achievement["aid"]
def check_question(question=None, answer=None, data=None): data = join_kwargs(data, question=question, answer=answer) data['question'] = int(data['question']) validate(submission_schema, data) num = data.pop('question') answer = data.pop('answer') question = safe_fail(get_question, num=num) if not question: raise InternalException('Question not found') correct = question['answer'] in answer solved = safe_fail(has_solved, question['qid']) with get_conn() as cursor: uid = session['uid'] points = question['success'] if correct else -question['failure'] query = 'INSERT INTO `submissions` (`uid`, `qid`, `answer`, `points`, `correct`) VALUES (%s, %s, %s, %s, %s);' args = (uid, question['qid'], answer, points if not solved else 0, 1 if correct else 0) cursor.execute(query, args) if correct: return WebSuccess('Correct!', data={'url': get_next_question_url()}) else: return WebError('Incorrect')
def add_problem_feedback(pid, uid, feedback): """ Add user problem feedback to the database. Args: pid: the problem id uid: the user id feedback: the problem feedback. """ db = api.common.get_conn() #Make sure the problem actually exists. api.problem.get_problem(pid=pid) team = api.user.get_team(uid=uid) solved = pid in api.problem.get_solved_pids(tid=team["tid"]) validate(feedback_schema, feedback) db.problem_feedback.insert({ "pid": pid, "uid": uid, "tid": team["tid"], "solved": solved, "timestamp": datetime.utcnow(), "feedback": feedback }) api.achievement.process_achievements("review", {"uid": uid, "tid": team['tid'], "pid": pid})
def create_new_team_request(params, uid=None): """ Fulfills new team requests for users who have already registered. Args: team_name: The desired name for the team. Must be unique across users and teams. team_password: The team's password. Returns: True if successful, exception thrown elsewise. """ validate(new_team_schema, params) user = api.user.get_user(uid=uid) current_team = api.team.get_team(tid=user["tid"]) if current_team["team_name"] != user["username"]: raise InternalException( "You can only create one new team per user account!") desired_tid = create_team({ "team_name": params["team_name"], "password": params["team_password"], # The team's affiliation becomes the creator's affiliation. "affiliation": current_team["affiliation"], "eligible": True }) return join_team(params["team_name"], params["team_password"], user["uid"])
def create_group_request(params, uid=None): """ Creates a new group. Validates forms. All required arguments are assumed to be keys in params. Args: group-name: The name of the group Optional: uid: The uid of the user creating the group. If omitted, the uid will be grabbed from the logged in user. Returns: The new gid """ if uid is None: uid = api.user.get_user()["uid"] validate(register_group_schema, params) if safe_fail(get_group, name=params["group-name"], owner_uid=uid) is not None: raise WebException("A class with that name already exists!") return create_group(uid, params["group-name"])
def insert_problem(problem, sid=None): """ Inserts a problem into the database. Does sane validation. Args: Problem dict. score: points awarded for completing the problem. category: problem's category author: author of the problem description: description of the problem. Optional: version: version of the problem tags: list of problem tags. hints: hints for completing the problem. organization: Organization that author is associated with Returns: The newly created problem id. """ db = api.common.get_conn() validate(problem_schema, problem) # initially disable problems problem["disabled"] = True problem["pid"] = api.common.hash("{}-{}".format(problem["name"], problem["author"])) for instance in problem["instances"]: validate(instance_schema, instance) set_instance_ids(problem, sid) if safe_fail(get_problem, pid=problem["pid"]) is not None: # problem is already inserted, so update instead old_problem = copy(get_problem(pid=problem["pid"])) # leave all instances from different shell server instances = list(filter(lambda i: i["sid"] != sid, old_problem["instances"])) # add instances from this shell server instances.extend(problem["instances"]) problem["instances"] = instances # disable problems with zero instances problem["disabled"] = old_problem["disabled"] or len(problem["instances"]) == 0 # run the update update_problem(problem["pid"], problem) return if safe_fail(get_problem, name=problem["name"]) is not None: raise WebException("Problem with identical name \"{}\" already exists.".format(problem["name"])) db.problems.insert(problem) api.cache.fast_cache.clear() return problem["pid"]
def join_team_request(params): """ Validate and process a join_team request. Args: team_name team_password """ validate(join_team_schema, params) return join_team(params["team_name"], params["team_password"])
def change_group_settings(gid, settings): """ Replace the current settings with the supplied ones. """ db = api.common.get_conn() validate(group_settings_schema, settings) group = api.group.get_group(gid=gid) if group["settings"]["hidden"] and not settings["hidden"]: raise InternalException("You can not change a hidden group back to a public group.") db.groups.update({"gid": group["gid"]}, {"$set": {"settings": settings}})
def insert_problem(problem): """ Inserts a problem into the database. Does sane validation. Args: Problem dict. score: points awarded for completing the problem. category: problem's category description: description of the problem. grader: path relative to grader_base_path threshold: Amount of points necessary for a team to unlock this problem. Optional: disabled: True or False. Defaults to False. hint: hint for completing the problem. tags: list of problem tags. relatedproblems: list of related problems. weightmap: problem's unlock weightmap autogen: Whether or not the problem will be auto generated. Returns: The newly created problem id. """ db = api.common.get_conn() validate(problem_schema, problem) problem["disabled"] = problem.get("disabled", False) problem["pid"] = api.common.hash(problem["name"]) weightmap = {} if problem.get("weightmap"): for name, weight in problem["weightmap"].items(): name_hash = api.common.hash(name) weightmap[name_hash] = weight problem["weightmap"] = weightmap if safe_fail(get_problem, pid=problem["pid"]) is not None: raise WebException("Problem with identical pid already exists.") if safe_fail(get_problem, name=problem["name"]) is not None: raise WebException("Problem with identical name already exists.") db.problems.insert(problem) api.cache.fast_cache.clear() return problem["pid"]
def join_team_request(params): """ Validate and process a join_team request. Args: team_name team_password """ user = api.user.get_user() if user["teacher"]: raise InternalException("Teachers may not join teams!") validate(join_team_schema, params) return join_team(params["team_name"], params["team_password"])
def apply_vote(wid, direction): db = api.common.get_conn() tid = api.user.get_user()["tid"] uid = api.user.get_user()["uid"] vote = { 'wid': wid, 'uid': uid, 'tid': tid, 'up': direction } validate(vote_schema, vote) if wid in get_voted_on(uid, tid): db.writeup_votes.update({"wid":wid, "tid":tid, "uid":uid}, vote) else: db.writeup_votes.insert(vote) return get_score(wid)
def create_group_hook(): """ Creates a new group. Validates forms. All required arguments are assumed to be keys in params. """ params = api.common.flat_multi(request.form) validate(register_group_schema, params) # Current user is the prospective owner. team = api.user.get_team() if safe_fail(api.group.get_group, name=params["group-name"], owner_tid=team["tid"]) is not None: raise WebException("A group with that name already exists!") gid = api.group.create_group(team["tid"], params["group-name"]) return WebSuccess("Successfully created group.", data=gid)
def create_simple_user_request(params): """ Registers a new user and creates a team for them automatically. Validates all fields. Assume arguments to be specified in a dict. Args: username: user's username password: user's password firstname: user's first name lastname: user's first name email: user's email """ params["country"] = "US" validate(user_schema, params) if api.config.get_settings()["captcha"]["enable_captcha"] and not _validate_captcha(params): raise WebException("Incorrect captcha!") team_params = { "team_name": params["username"], "password": api.common.token(), "eligible": True } tid = api.team.create_team(team_params) if tid is None: raise InternalException("Failed to create new team") team = api.team.get_team(tid=tid) # Create new user uid = create_user( params["username"], params["firstname"], params["lastname"], params["email"], hash_password(params["password"]), team["tid"], country=params["country"], ) if uid is None: raise InternalException("There was an error during registration.") return uid
def insert_problem(problem): """ Inserts a problem into the database. Does sane validation. Args: Problem dict. score: points awarded for completing the problem. category: problem's category author: author of the problem description: description of the problem. Optional: version: version of the problem tags: list of problem tags. hints: hints for completing the problem. organization: Organization that author is associated with Returns: The newly created problem id. """ db = api.common.get_conn() validate(problem_schema, problem) for instance in problem["instances"]: validate(instance_schema, instance) # initially disable problems problem["disabled"] = True problem["pid"] = api.common.hash("{}-{}".format(problem["name"], problem["author"])) if safe_fail(get_problem, pid=problem["pid"]) is not None: # problem is already inserted, so update instead old_problem = get_problem(pid=problem["pid"]) problem["disabled"] = old_problem["disabled"] assert len(problem["instances"]) >= len(old_problem["instances"]), "Cannot update problem with fewer instances." update_problem(problem["pid"], problem) return if safe_fail(get_problem, name=problem["name"]) is not None: raise WebException("Problem with identical name \"{}\" already exists.".format(problem["name"])) db.problems.insert(problem) api.cache.fast_cache.clear() return problem["pid"]
def update_bundle(bid, updates): """ Updates the bundle object in the database with the given updates. """ db = api.common.get_conn() bundle = db.bundles.find_one({"bid": bid}, {"_id": 0}) if bundle is None: raise WebException("Bundle with bid {} does not exist".format(bid)) # pop the bid temporarily to check with schema bid = bundle.pop("bid") bundle.update(updates) validate(bundle_schema, bundle) bundle["bid"] = bid db.bundles.update({"bid": bid}, {"$set": bundle})
def login(username=None, password=None, data=None): data = join_kwargs(data, username=username, password=password) validate(login_schema, data) username = data.pop('username') password = data.pop('password') user = safe_fail(get_user, name=username) if user is not None and confirm_password(password, user['password_hash']): if user['uid'] is not None: session['uid'] = user['uid'] session.permanent = True else: raise WebException('Login error') else: raise WebException('Username or password incorrect')
def update_bundle(bid, updates): """ Updates the bundle object in the database with the given updates. """ db = api.common.get_conn() bundle = db.bundles.find_one({"bid" : bid}, {"_id": 0}) if bundle is None: raise WebException("Bundle with bid {} does not exist".format(bid)) # pop the bid temporarily to check with schema bid = bundle.pop("bid") bundle.update(updates) validate(bundle_schema, bundle) bundle["bid"] = bid db.bundles.update({"bid": bid}, {"$set": bundle})
def create_user(username=None, password=None, data=None): data = join_kwargs(data, username=username, password=password) validate(login_schema, data) username = data.pop('username') password = data.pop('password') uid = api.common.token() with get_conn() as cursor: query = 'INSERT INTO `users` (`uid`, `username`, `password_hash`) VALUES (%s, %s, %s);' try: cursor.execute(query, (uid, username, hash_password(password))) except pymysql.err.IntegrityError as e: raise WebException('User already exists') return uid
def join_group_hook(): """ Tries to place a team into a group. Validates forms. All required arguments are assumed to be keys in params. """ params = api.common.flat_multi(request.form) validate(join_group_schema, params) owner_team = safe_fail(api.team.get_team, name=params["group-owner"]) if not owner_team: raise WebException("No teacher exists with that name!") if safe_fail(api.group.get_group, name=params["group-name"], owner_tid=owner_team["tid"]) is None: raise WebException("No classroom exists with that name!") group = api.group.get_group(name=params["group-name"], owner_tid=owner_team["tid"]) group_settings = api.group.get_group_settings(gid=group["gid"]) team = api.team.get_team() if group_settings["email_filter"]: for member_uid in api.team.get_team_uids(tid=team["tid"]): member = api.user.get_user(uid=member_uid) if not api.user.verify_email_in_whitelist( member["email"], group_settings["email_filter"]): raise WebException( "{}'s email does not belong to the whitelist for that classroom. Your team may not join this classroom at this time." .format(member["username"])) roles = api.group.get_roles_in_group(group["gid"], tid=team["tid"]) if roles["teacher"] or roles["member"]: raise WebException("Your team is already a member of that classroom!") api.group.join_group(group["gid"], team["tid"]) return WebSuccess("Successfully joined classroom")
def insert_bundle(bundle): """ Inserts the bundle into the database after validating it with the bundle_schema """ db = api.common.get_conn() validate(bundle_schema, bundle) bid = api.common.hash("{}-{}".format(bundle["name"], bundle["author"])) if safe_fail(get_bundle, bid) is not None: # bundle already exists, update it instead update_bundle(bid, bundle) return bundle["bid"] = bid bundle["dependencies_enabled"] = False db.bundles.insert(bundle)
def login(username, password): """ Authenticates a user. """ # Read in submitted username and password validate(user_login_schema, {"username": username, "password": password}) user = safe_fail(api.user.get_user, name=username) if user is None: raise WebException("Incorrect username.") if user.get("disabled", False): raise WebException("This account has been disabled.") if not user["verified"]: raise WebException("This account has not been verified yet.") if confirm_password(password, user['password_hash']): if not user["verified"]: try: api.email.send_user_verification_email(username) raise WebException( "This account is not verified. An additional email has been sent to {}." .format(user["email"])) except InternalException as e: raise WebException( "You have hit the maximum number of verification emails. Please contact support." ) if debug_disable_general_login: if session.get('debugaccount', False): raise WebException( "Correct credentials! But the game has not started yet...") if user['uid'] is not None: session['uid'] = user['uid'] session.permanent = True else: raise WebException("Login Error") else: raise WebException("Incorrect password")
def add_server(params): """ Add a shell server to the pool of servers. First server is automatically assigned server_number 1 (yes, 1-based numbering) if not otherwise specified. Args: params: A dict containing: host port username password server_number Returns: The sid. """ db = api.common.get_conn() validate(server_schema, params) if isinstance(params["port"], str): params["port"] = int(params["port"]) if isinstance(params.get("server_number"), str): params["server_number"] = int(params["server_number"]) if safe_fail(get_server, name=params["name"]) is not None: raise WebException("Shell server with this name already exists") params["sid"] = api.common.hash(params["name"]) # Automatically set first added server as server_number 1 if db.shell_servers.count() == 0: params["server_number"] = params.get("server_number", 1) db.shell_servers.insert(params) return params["sid"]
def leave_group_hook(): """ Tries to remove a team from a group. Validates forms. All required arguments are assumed to be keys in params. """ params = api.common.flat_multi(request.form) validate(leave_group_schema, params) owner_team = api.team.get_team(name=params["group-owner"]) group = api.group.get_group(name=params["group-name"], owner_tid=owner_team["tid"]) team = api.user.get_team() roles = api.group.get_roles_in_group(group["gid"], tid=team["tid"]) if not roles["member"] and not roles["teacher"]: raise WebException("Your team is not a member of that class!") api.group.leave_group(group["gid"], team["tid"]) return WebSuccess("Successfully left group.")