Esempio n. 1
0
def user_memberships_by_username_put(username) -> Response:
    """
    Update the team and group memberships of a user.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the PUT API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    if username == jwt_username:
        current_app.logger.info(
            f'User {jwt_username} is updating their memberships.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update the memberships of user {username}.'
        )
        response = jsonify({
            'self':
            f'/v2/users/memberships/{username}',
            'updated':
            False,
            'error':
            f'User {jwt_username} is not authorized to update the memberships of user {username}.'
        })
        response.status_code = 400
        return response

    membership_data: dict = request.get_json()
    teams_joined = membership_data.get('teams_joined')
    teams_left = membership_data.get('teams_left')
    groups_joined = membership_data.get('groups_joined')
    groups_left = membership_data.get('groups_left')

    committed: bool = False

    try:
        committed = TeamMemberDao.update_user_memberships(
            username, teams_joined, teams_left, groups_joined, groups_left)
    except SQLAlchemyError as e:
        current_app.logger.error(str(e))

    if committed:
        response = jsonify({
            'self': f'/v2/users/memberships/{username}',
            'updated': True,
        })
        response.status_code = 201
        return response
    else:
        response = jsonify({
            'self': f'/v2/users/memberships/{username}',
            'updated': False,
            'error': "failed to update the user's memberships"
        })
        response.status_code = 500
        return response
Esempio n. 2
0
def user_teams_by_username_get(username) -> Response:
    """
    Get the team memberships for a user.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the GET API request.
    """
    teams: ResultProxy = TeamMemberDao.get_user_teams(username=username)
    team_list = []

    for team in teams:
        team_list.append({
            'team_name': team['team_name'],
            'title': team['title'],
            'status': team['status'],
            'user': team['user']
        })

    response = jsonify({
        'self': f'/v2/users/teams/{username}',
        'teams': team_list
    })
    response.status_code = 200
    return response
Esempio n. 3
0
def user_memberships_by_username_get(username) -> Response:
    """
    Get the team and group memberships for a user.
    :param username: Username that uniquely identifies a user.
    :return: A response object for the GET API request.
    """
    teams: ResultProxy = TeamMemberDao.get_user_teams(username=username)
    membership_list = []

    for team in teams:
        groups: ResultProxy = GroupMemberDao.get_user_groups_in_team(
            username=username, team_name=team['team_name'])
        membership_list.append({
            'team_name':
            team['team_name'],
            'title':
            team['title'],
            'status':
            team['status'],
            'user':
            team['user'],
            'groups': [{
                'group_name': group['group_name'],
                'group_title': group['group_title'],
                'group_id': group['group_id'],
                'status': group['status'],
                'user': group['user']
            } for group in groups]
        })

    response = jsonify({
        'self': f'/v2/users/memberships/{username}',
        'memberships': membership_list
    })
    response.status_code = 200
    return response
Esempio n. 4
0
def team_members_by_team_name_get(team_name) -> Response:
    """
    Get the members of a team based on the team name.
    :param team_name: Unique name of a team.
    :return: A response object for the GET API request.
    """
    team_members_result: ResultProxy = TeamMemberDao.get_team_members(team_name=team_name)

    if team_members_result is None or team_members_result.rowcount == 0:
        response = jsonify({
            'self': f'/v2/teams/members/{team_name}',
            'team': f'/v2/teams/{team_name}',
            'team_members': None,
            'error': 'the team does not exist or it has no members'
        })
        response.status_code = 400
        return response
    else:
        team_members_list = [{
            'username': member.username,
            'first': member.first,
            'last': member.last,
            'member_since': member.member_since,
            'user': member.user,
            'status': member.status,
            'deleted': member.deleted
        }
            for member in team_members_result]

        response = jsonify({
            'self': f'/v2/teams/members/{team_name}',
            'group': f'/v2/teams/{team_name}',
            'team_members': team_members_list
        })
        response.status_code = 200
        return response
Esempio n. 5
0
def user_post() -> Response:
    """
    Create a new user.
    :return: A response object for the POST API request.
    """
    user_data: dict = request.get_json()

    def create_validation_error_response(message: str):
        """
        Reusable 400 HTTP error response for the users POST request.
        :param message: Message sent in the response JSON's 'error' field.
        :return: An HTTP response object.
        """
        error_response = jsonify({
            'self': f'/v2/users',
            'added': False,
            'user': None,
            'error': message
        })
        error_response.status_code = 400
        return error_response

    if user_data is None:
        return create_validation_error_response(
            "The request body isn't populated.")

    user_to_add = User(user_data)

    if None in [
            user_to_add.username, user_to_add.first, user_to_add.last,
            user_to_add.password, user_to_add.activation_code,
            user_to_add.email
    ]:
        return create_validation_error_response(
            "'username', 'first', 'last', 'email', 'password', and 'activation_code' are required fields"
        )

    if len(user_to_add.password) < 6:
        return create_validation_error_response(
            "Password must contain at least 6 characters.")

    username_pattern = re.compile('^[a-zA-Z0-9]+$')

    if not username_pattern.match(user_to_add.username):
        return create_validation_error_response(
            "Username can only contain Roman characters and numbers.")

    email_pattern = re.compile(
        '^(([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\\.([a-zA-Z])+([a-zA-Z])+)?$')

    if not email_pattern.match(user_to_add.email):
        return create_validation_error_response(
            "The email address is invalid.")

    # Passwords must be hashed before stored in the database
    password = user_to_add.password
    hashed_password = flask_bcrypt.generate_password_hash(password).decode(
        'utf-8')
    user_to_add.password = hashed_password

    activation_code_count = CodeDao.get_code_count(
        activation_code=user_to_add.activation_code)

    if activation_code_count == 1:
        now = datetime.now()
        user_to_add.member_since = now.date()
        user_to_add.created_date = now
        user_to_add.last_signin = now
        user_to_add.created_app = 'saints-xctf-api'
        user_to_add.created_user = None
        user_to_add.modified_date = None
        user_to_add.modified_app = None
        user_to_add.modified_user = None
        user_to_add.deleted_date = None
        user_to_add.deleted_app = None
        user_to_add.deleted_user = None
        user_to_add.deleted = False

        # First, add the user since its activation code is valid.
        UserDao.add_user(user_to_add)

        # Second, set the initial team and group for the user.
        code: Code = ActivationCodeDao.get_activation_code(
            user_to_add.activation_code)

        initial_group_id = int(code.group_id)
        initial_group: Group = GroupDao.get_group_by_id(initial_group_id)
        initial_team: Team = TeamDao.get_team_by_group_id(initial_group_id)

        TeamMemberDao.set_initial_membership(
            username=user_to_add.username,
            team_name=initial_team.name,
            group_id=initial_group_id,
            group_name=initial_group.group_name)

        # Third, remove the activation code so it cant be used again.
        CodeDao.remove_code(code)

        added_user = UserDao.get_user_by_username(user_to_add.username)

        if added_user is None:
            response = jsonify({
                'self':
                '/v2/users',
                'added':
                False,
                'user':
                None,
                'error':
                'An unexpected error occurred creating the user.'
            })
            response.status_code = 500
            return response
        else:
            response = jsonify({
                'self': '/v2/users',
                'added': True,
                'user': UserData(added_user).__dict__,
                'new_user': f'/v2/users/{added_user.username}'
            })
            response.status_code = 201
            return response
    else:
        current_app.logger.error(
            'Failed to create new User: The Activation Code does not exist.')
        response = jsonify({
            'self':
            '/v2/users',
            'added':
            False,
            'user':
            None,
            'error':
            'The activation code is invalid or expired.'
        })
        response.status_code = 400
        return response
Esempio n. 6
0
def group_members_by_group_id_and_username_delete(group_id: str,
                                                  username: str) -> Response:
    """
    Soft delete a group membership.  The membership is identified by a group's identifier and a user's username.
    :param group_id: Unique id which identifies a group within a team.
    :param username: Unique name for a user.
    :return: A response object for the DELETE API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    group_member: GroupMember = GroupMemberDao.get_group_member(
        group_id=int(group_id), username=jwt_username)

    if group_member is not None and group_member.user == 'admin' and group_member.status == 'accepted':
        current_app.logger.info(
            f'Admin user {jwt_username} is deleting the group membership for user {username} in group with id '
            f'{group_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to delete the group membership for user {username} in group with '
            f'id {group_id}.')
        response = jsonify({
            'self':
            f'/v2/groups/members/{group_id}/{username}',
            'deleted':
            False,
            'error':
            f'User {jwt_username} is not authorized to delete the group membership for user {username} in '
            f'group with id {group_id}.'
        })
        response.status_code = 400
        return response

    membership_deleted = GroupMemberDao.soft_delete_group_member(
        int(group_id), username)

    if membership_deleted:
        team: Team = TeamDao.get_team_by_group_id(int(group_id))
        user_groups: ResultProxy = GroupMemberDao.get_user_groups_in_team(
            username, team.name)

        # If the user has no more group memberships in this team, remove them from the team.
        if user_groups.rowcount == 0:
            TeamMemberDao.update_user_memberships(username=username,
                                                  teams_joined=[],
                                                  teams_left=[team.name],
                                                  groups_joined=[],
                                                  groups_left=[])

        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'deleted': True,
        })
        response.status_code = 204
        return response
    else:
        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'deleted': False,
            'error': 'Failed to delete the group membership.'
        })
        response.status_code = 500
        return response
Esempio n. 7
0
def group_members_by_group_id_and_username_put(group_id: str,
                                               username: str) -> Response:
    """
    Update a group membership.  The membership is identified by a group's identifier and a user's username.
    :param group_id: Unique id which identifies a group within a team.
    :param username: Unique name for a user.
    :return: A response object for the PUT API request.
    """
    jwt_claims: dict = get_claims(request)
    jwt_username = jwt_claims.get('sub')

    group_member: GroupMember = GroupMemberDao.get_group_member(
        group_id=int(group_id), username=jwt_username)

    if group_member is not None and group_member.user == 'admin' and group_member.status == 'accepted':
        current_app.logger.info(
            f'Admin user {jwt_username} is updating the group membership for user {username} in group with id '
            f'{group_id}.')
    else:
        current_app.logger.info(
            f'User {jwt_username} is not authorized to update the group membership for user {username} in group with '
            f'id {group_id}.')
        response = jsonify({
            'self':
            f'/v2/groups/members/{group_id}/{username}',
            'updated':
            False,
            'group_member':
            None,
            'error':
            f'User {jwt_username} is not authorized to update the group membership for user {username} in '
            f'group with id {group_id}.'
        })
        response.status_code = 400
        return response

    group_member_data: dict = request.get_json()
    status = group_member_data.get('status')
    user = group_member_data.get('user')

    is_updated = GroupMemberDao.update_group_member(int(group_id), username,
                                                    status, user)

    if is_updated:
        team: Team = TeamDao.get_team_by_group_id(int(group_id))
        team_membership: ResultProxy = TeamMemberDao.get_user_team_membership(
            username=username, team_name=team.name)

        if team_membership.rowcount > 0:
            for membership in team_membership:
                if membership.user != 'accepted':
                    TeamMemberDao.accept_user_team_membership(
                        username=username,
                        team_name=team.name,
                        updating_username=jwt_username)

        updated_group_member = GroupMemberDao.get_group_member(
            int(group_id), username)
        updated_group_member_dict: dict = GroupMemberData(
            updated_group_member).__dict__

        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'updated': True,
            'group_member': updated_group_member_dict
        })
        response.status_code = 200
        return response
    else:
        response = jsonify({
            'self': f'/v2/groups/members/{group_id}/{username}',
            'updated': False,
            'group_member': None,
            'error': 'The group membership failed to update.'
        })
        response.status_code = 500
        return response