Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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")
Ejemplo n.º 3
0
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")
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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")
Ejemplo n.º 6
0
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")
Ejemplo n.º 7
0
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})
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
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"])
Ejemplo n.º 11
0
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")
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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})
Ejemplo n.º 14
0
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"]
Ejemplo n.º 15
0
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})
Ejemplo n.º 16
0
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"]
Ejemplo n.º 17
0
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")
Ejemplo n.º 18
0
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"])
Ejemplo n.º 19
0
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"])
Ejemplo n.º 20
0
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'])
Ejemplo n.º 21
0
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"]
Ejemplo n.º 22
0
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"])
Ejemplo n.º 23
0
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})
Ejemplo n.º 24
0
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"])
Ejemplo n.º 25
0
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"]
Ejemplo n.º 26
0
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')
Ejemplo n.º 27
0
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})
Ejemplo n.º 28
0
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"]
Ejemplo n.º 29
0
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")
Ejemplo n.º 30
0
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"])
Ejemplo n.º 31
0
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"])
Ejemplo n.º 32
0
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"]
Ejemplo n.º 33
0
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"])
Ejemplo n.º 34
0
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"])
Ejemplo n.º 35
0
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}})
Ejemplo n.º 36
0
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"]
Ejemplo n.º 37
0
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"]
Ejemplo n.º 38
0
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"])
Ejemplo n.º 39
0
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)
Ejemplo n.º 40
0
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"])
Ejemplo n.º 41
0
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)
Ejemplo n.º 42
0
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
Ejemplo n.º 43
0
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"]
Ejemplo n.º 44
0
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})
Ejemplo n.º 45
0
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')
Ejemplo n.º 46
0
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})
Ejemplo n.º 47
0
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
Ejemplo n.º 48
0
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)
Ejemplo n.º 49
0
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")
Ejemplo n.º 50
0
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)
Ejemplo n.º 51
0
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)
Ejemplo n.º 52
0
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")
Ejemplo n.º 53
0
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"]
Ejemplo n.º 54
0
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.")