Exemplo n.º 1
0
def reassign_teams(include_assigned=False):
    db = api.common.get_conn()

    if include_assigned:
        teams = api.team.get_all_teams(include_ineligible=True)
    else:
        teams = list(
            db.teams.find({"server_number": {
                "$exists": False
            }}, {
                "_id": 0,
                "tid": 1
            }))

    for team in teams:
        old_server_number = team.get("server_number")
        server_number = get_assigned_server_number(new_team=False,
                                                   tid=team["tid"])
        if old_server_number != server_number:
            db.teams.update(
                {'tid': team["tid"]},
                {'$set': {
                    'server_number': server_number,
                    'instances': {}
                }})
            # Re-assign instances
            safe_fail(api.problem.get_visible_problems, team["tid"])

    return len(teams)
Exemplo n.º 2
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')
Exemplo n.º 3
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"]
Exemplo n.º 4
0
def reassign_teams(include_assigned=False):
    db = api.common.get_conn()

    if include_assigned:
        teams = api.team.get_all_teams(show_ineligible=True)
    else:
        teams = list(
            db.teams.find({
                "server_number": {
                    "$exists": False
                }
            }, {
                "_id": 0,
                "tid": 1
            }))

    for team in teams:
        old_server_number = team.get("server_number")
        server_number = get_assigned_server_number(
            new_team=False, tid=team["tid"])
        if old_server_number != server_number:
            db.teams.update({
                'tid': team["tid"]
            }, {'$set': {
                'server_number': server_number,
                'instances': {}
            }})
            # Re-assign instances
            safe_fail(api.problem.get_visible_problems, team["tid"])

    return len(teams)
Exemplo n.º 5
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"]
Exemplo n.º 6
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"]
Exemplo n.º 7
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"]
Exemplo n.º 8
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"]
Exemplo n.º 9
0
def authorize_role(role=None):
    """
    This route is used to ensure sensitive static content is witheld from withheld from clients.
    """

    if role == "user" and safe_fail(api.user.get_user):
        return "Client is logged in.", 200
    elif role == "teacher" and safe_fail(api.user.is_teacher):
        return "Client is a teacher.", 200
    elif role == "admin" and safe_fail(api.user.is_admin):
        return "Client is an administrator.", 200
    elif role == "anonymous":
        return "Client is authorized.", 200
    else:
        return "Client is not authorized.", 401
Exemplo n.º 10
0
def authorize_role(role=None):
    """
    This route is used to ensure sensitive static content is witheld from withheld from clients.
    """

    if role == "user" and safe_fail(api.user.get_user):
        return "Client is logged in.", 200
    elif role == "teacher" and safe_fail(api.user.is_teacher):
        return "Client is a teacher.", 200
    elif role == "admin" and safe_fail(api.user.is_admin):
        return "Client is an administrator.", 200
    elif role == "anonymous":
        return "Client is authorized.", 200
    else:
        return "Client is not authorized.", 401
Exemplo 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")
Exemplo n.º 12
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"])
Exemplo n.º 13
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"])
Exemplo n.º 14
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})
Exemplo n.º 15
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)
Exemplo n.º 16
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})
Exemplo n.º 17
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"]
Exemplo n.º 18
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'])
Exemplo n.º 19
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
Exemplo n.º 20
0
def create_user(username, firstname, lastname, email, password_hash, tid,
                teacher=False, country="US", admin=False, verified=False):
    """
    This inserts a user directly into the database. It assumes all data is valid.

    Args:
        username: user's username
        firstname: user's first name
        lastname: user's last name
        email: user's email
        password_hash: a hash of the user's password
        tid: the team id to join
        teacher: whether this account is a teacher
    Returns:
        Returns the uid of the newly created user
    """

    db = api.common.get_conn()
    settings = api.config.get_settings()
    uid = api.common.token()

    if safe_fail(get_user, name=username) is not None:
        raise InternalException("User already exists!")

    max_team_size = api.config.get_settings()["max_team_size"]

    updated_team = db.teams.find_and_modify(
        query={"tid": tid, "size": {"$lt": max_team_size}},
        update={"$inc": {"size": 1}},
        new=True)

    if not updated_team:
        raise InternalException("There are too many users on this team!")

    #All teachers are admins.
    if admin or db.users.count() == 0:
        admin = True
        teacher = True

    user = {
        'uid': uid,
        'firstname': firstname,
        'lastname': lastname,
        'username': username,
        'email': email,
        'password_hash': password_hash,
        'tid': tid,
        'teacher': teacher,
        'admin': admin,
        'disabled': False,
        'country': country,
        'verified': not settings["email"]["email_verification"] or verified,
    }

    db.users.insert(user)

    if settings["email"]["email_verification"] and not user["verified"]:
        api.email.send_user_verification_email(username)

    return uid
Exemplo n.º 21
0
    def test_create_user_request_general_validation(self):
        """
        Tests the registration form validation functionality.

        Covers:
            partially: user.create_user_request
        """

        #Generally invalidate every length requirement
        for bad_length_mod in [0, 200]:
            for user_blueprint in [self.new_team_user, self.existing_team_user]:
                for key in user_blueprint.keys():
                    sheep_user = user_blueprint.copy()

                    #Make sure to keep the basic properties

                    if key == "create-new-team":
                        continue
                    elif key == "email":
                        sheep_user[key] = "x@x." + "x" * bad_length_mod
                    else:
                        sheep_user[key] = "A" * bad_length_mod

                    if sheep_user["create-new-team"] != "true" and \
                    safe_fail(api.team.get_team, name=sheep_user["team-name-existing"]) is None:
                        team = self.base_team.copy()
                        team['team_name'], team['password'] = \
                                sheep_user["team-name-existing"], sheep_user["team-password-existing"]
                        api.team.create_team(team)

                    with pytest.raises(WebException):
                        api.user.create_user_request(sheep_user)
                        assert False, "Validation failed to catch {} length {}".format(bad_length_mod, key)
Exemplo n.º 22
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"]
Exemplo n.º 23
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")
Exemplo n.º 24
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"])
Exemplo n.º 25
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")
Exemplo n.º 26
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)
Exemplo n.º 27
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"])
Exemplo n.º 28
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
Exemplo n.º 29
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
Exemplo n.º 30
0
def analyze_problems():
    """
    Checks the sanity of inserted problems.
    Includes weightmap and grader verification.

    Returns:
        A list of error strings describing the problems.
    """

    grader_missing_error = "{}: Missing grader at '{}'."
    unknown_weightmap_pid = "{}: Has weightmap entry '{}' which does not exist."

    problems = get_all_problems()

    errors = []

    for problem in problems:
        if not isfile(join(grader_base_path, problem["grader"])):
            errors.append(
                grader_missing_error.format(problem["name"],
                                            problem["grader"]))

        for pid in problem["weightmap"].keys():
            if safe_fail(get_problem, pid=pid) is None:
                errors.append(
                    unknown_weightmap_pid.format(problem["name"], pid))
    return errors
Exemplo n.º 31
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"]
Exemplo n.º 32
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")
Exemplo n.º 33
0
    def test_unicode_fields(self):
        """
        Ensures that unicode characters will work in user and team fields
        """

        team_name = u"Team \N{GREEK CAPITAL LETTER DELTA}"
        username = u"User \u039B"

        user1 = self.new_team_user.copy()
        user2 = self.existing_team_user.copy()

        user1["username"] = username
        user1["team-name-new"] =  team_name
        user2["team-name-existing"] = team_name

        api.user.create_user_request(user1)
        api.user.create_user_request(user2)

        assert safe_fail(api.user.get_user, name=username) is not None, "Cannot look up unicode username"
        assert safe_fail(api.team.get_team, name=team_name) is not None, "Cannot look up unicode team name"
Exemplo n.º 34
0
def check_create_username(username):
    """
    Ensures that the username is not already used and raises and error if it is

    Args:
        username: username to check
    Returns:
        Nothing
    """
    if safe_fail(get_user, name=username) is not None:
        raise InternalException("User already exists!")
Exemplo n.º 35
0
def create_user(username, firstname, lastname, email, password_hash, tid, teacher=False,
                background="undefined", country="undefined", receive_ctf_emails=False):
    """
    This inserts a user directly into the database. It assumes all data is valid.

    Args:
        username: user's username
        firstname: user's first name
        lastname: user's last name
        email: user's email
        password_hash: a hash of the user's password
        tid: the team id to join
        teacher: whether this account is a teacher
    Returns:
        Returns the uid of the newly created user
    """

    db = api.common.get_conn()
    uid = api.common.token()

    if safe_fail(get_user, name=username) is not None:
        raise InternalException("User already exists!")

    updated_team = db.teams.find_and_modify(
        query={"tid": tid, "size": {"$lt": api.team.max_team_users}},
        update={"$inc": {"size": 1}},
        new=True)

    if not updated_team:
        raise InternalException("There are too many users on this team!")

    user = {
        'uid': uid,
        'firstname': firstname,
        'lastname': lastname,
        'username': username,
        'email': email,
        'password_hash': password_hash,
        'tid': tid,
        'teacher': teacher,
        'avatar': 3,
        'eventid': 0,
        'disabled': False,
        'level': 'Not Started',
        'background': background,
        'country': country,
        'receive_ctf_emails': receive_ctf_emails
    }

    db.users.insert(user)

    return uid
Exemplo n.º 36
0
def is_logged_in():
    """
    Check if the user is currently logged in.

    Returns:
        True if the user is logged in, false otherwise.
    """

    logged_in = "uid" in session
    if logged_in and not safe_fail(api.user.get_user, uid=session["uid"]):
        logout()
        return False
    return logged_in
Exemplo n.º 37
0
def is_logged_in():
    """
    Check if the user is currently logged in.

    Returns:
        True if the user is logged in, false otherwise.
    """

    logged_in = "uid" in session
    if logged_in and not safe_fail(api.user.get_user, uid=session["uid"]):
        logout()
        return False
    return logged_in
Exemplo n.º 38
0
def is_logged_in():
    """
    Check if the user is currently logged in.

    Returns:
        True if the user is logged in, false otherwise.
    """

    logged_in = "uid" in session
    if logged_in:
        user = safe_fail(api.user.get_user, uid=session["uid"])
        if not user or user["disabled"]:
            logout()
            return False
    return logged_in
Exemplo n.º 39
0
def is_logged_in():
    """
    Check if the user is currently logged in.

    Returns:
        True if the user is logged in, false otherwise.
    """

    logged_in = "uid" in session
    if logged_in:
        user = safe_fail(api.user.get_user, uid=session["uid"])
        if not user or user["disabled"]:
            logout()
            return False
    return logged_in
Exemplo n.º 40
0
    def test_unicode_fields(self):
        """
        Ensures that unicode characters will work in user and team fields
        """

        team_name = u"Team \N{GREEK CAPITAL LETTER DELTA}"
        username = u"User \u039B"

        user1 = self.new_team_user.copy()
        user2 = self.existing_team_user.copy()

        user1["username"] = username
        user1["team-name-new"] = team_name
        user2["team-name-existing"] = team_name

        api.user.create_user_request(user1)
        api.user.create_user_request(user2)

        assert safe_fail(
            api.user.get_user,
            name=username) is not None, "Cannot look up unicode username"
        assert safe_fail(
            api.team.get_team,
            name=team_name) is not None, "Cannot look up unicode team name"
Exemplo n.º 41
0
def get_next_question_url(num=None, uid=None):
    if not num:
        if not uid:
            if 'uid' not in session:
                raise InternalException('Need uid to see next')
            uid = session['uid']
        scores = api.user.get_user_scores()
        num = scores['num'] or 0

    next_question = safe_fail(get_question, num=int(num)+1)

    if not next_question:
        url = '/survived'
    else:
        url = 'question%d'%(next_question['num'], )

    return url
Exemplo n.º 42
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)
Exemplo n.º 43
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')
Exemplo n.º 44
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)
Exemplo n.º 45
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)
Exemplo n.º 46
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)
Exemplo n.º 47
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")
Exemplo n.º 48
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")
Exemplo n.º 49
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"]
Exemplo n.º 50
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"]
Exemplo n.º 51
0
    def test_create_user_request_general_validation(self):
        """
        Tests the registration form validation functionality.

        Covers:
            partially: user.create_user_request
        """

        #Generally invalidate every length requirement
        for bad_length_mod in [0, 200]:
            for user_blueprint in [
                    self.new_team_user, self.existing_team_user
            ]:
                for key in user_blueprint.keys():
                    sheep_user = user_blueprint.copy()

                    #Make sure to keep the basic properties

                    if key == "create-new-team":
                        continue
                    elif key == "email":
                        sheep_user[key] = "x@x." + "x" * bad_length_mod
                    else:
                        sheep_user[key] = "A" * bad_length_mod

                    if sheep_user["create-new-team"] != "true" and \
                    safe_fail(api.team.get_team, name=sheep_user["team-name-existing"]) is None:
                        team = self.base_team.copy()
                        team['team_name'], team['password'] = \
                                sheep_user["team-name-existing"], sheep_user["team-password-existing"]
                        api.team.create_team(team)

                    with pytest.raises(WebException):
                        api.user.create_user_request(sheep_user)
                        assert False, "Validation failed to catch {} length {}".format(
                            bad_length_mod, key)
Exemplo n.º 52
0
def analyze_problems():
    """
    Checks the sanity of inserted problems.
    Includes weightmap and grader verification.

    Returns:
        A list of error strings describing the problems.
    """

    grader_missing_error = "{}: Missing grader at '{}'."
    unknown_weightmap_pid = "{}: Has weightmap entry '{}' which does not exist."

    problems = get_all_problems()

    errors = []

    for problem in problems:
        if not isfile(join(grader_base_path, problem["grader"])):
            errors.append(grader_missing_error.format(problem["name"], problem["grader"]))

        for pid in problem["weightmap"].keys():
            if safe_fail(get_problem, pid=pid) is None:
                errors.append(unknown_weightmap_pid.format(problem["name"], pid))
    return errors
Exemplo n.º 53
0
        ("Email must be between 5 and 50 characters.", [str, Length(min=5, max=50)]),
        ("Your email does not look like an email address.", [_check_email_format])
    ),
    Required('firstname'): check(
        ("First Name must be between 1 and 50 characters.", [str, Length(min=1, max=50)])
    ),
    Required('lastname'): check(
        ("Last Name must be between 1 and 50 characters.", [str, Length(min=1, max=50)])
    ),
    Required('country'): check(
        ("Please select a country", [str, Length(min=2, max=2)])
    ),
    Required('username'): check(
        ("Usernames must be between 3 and 20 characters.", [str, Length(min=3, max=20)]),
        ("This username already exists.", [
            lambda name: safe_fail(get_user, name=name) is None])
    ),
    Required('password'):
        check(("Passwords must be between 3 and 20 characters.", [str, Length(min=3, max=20)])
    ),
    Required('background'):
        check(("You must provide your background!", [str, Length(min=3, max=20)])
    )
}, extra=True)

new_eligible_team_schema = Schema({
    Required('team-name-new'): check(
        ("The team name must be between 3 and 40 characters.", [str, Length(min=3, max=40)]),
        ("A team with that name already exists.", [
            lambda name: safe_fail(api.team.get_team, name=name) is None])
    ),