Ejemplo n.º 1
0
    def delete_helper(self, team_name, user_id) -> ResponseTuple:
        """
        Permanently delete a team.

        :param team_name: Name of team to be deleted
        :param user_id: Slack ID of user who called command
        :return: error message if user has insufficient permission level or
                 team deleted unsuccessfully, otherwise return success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            teams = self.facade.query(Team, [('github_team_name', team_name)])
            if len(teams) != 1:
                return self.lookup_error, 200
            team = teams[0]
            if not check_permissions(command_user, team):
                return self.permission_error, 200
            self.facade.delete(Team, team.github_team_id)
            self.gh.org_delete_team(int(team.github_team_id))
            return f"Team {team_name} deleted", 200
        except LookupError:
            return self.lookup_error, 200
        except GithubAPIException as e:
            logging.error("team delete unsuccessful")
            return f"Team delete was unsuccessful with " \
                   f"the following error: {e.data}", 200
Ejemplo n.º 2
0
    def team_delete(self, caller_id: str, gh_team_name: str) -> None:
        """
        Permanently delete a team.

        :param gh_team_name: Github team name of the team to delete
        :param caller_id: Slack ID of user who called command
        :raises: LookupError if the calling user or the team to delete could
                 not be found
        :raises: RuntimeError if more than one team has the specified
                 team name
        :raises: PermissionError if the calling user does not have sufficient
                 permissions to delete the specified team
        """
        logging.info("Team delete command API called")
        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        team = db_utils.get_team_by_name(self._db_facade, gh_team_name)

        if not check_permissions(command_user, team):
            msg = f"User with ID {caller_id} has insufficient permissions" \
                f" to delete team {gh_team_name}"
            logging.error(msg)
            raise PermissionError(msg)

        self._gh_interface.org_delete_team(int(team.github_team_id))

        self._db_facade.delete(Team, team.github_team_id)
        logging.info(f"{gh_team_name} successfully deleted")
Ejemplo n.º 3
0
    def edit_helper(self, param_list, user_id) -> ResponseTuple:
        """
        Edit the properties of a specific team.

        Team leads can only edit the teams that they are a part of, but admins
        can edit any teams.

        :param param_list: List of parameters for editing team
        :param user_id: Slack ID of user who called command
        :return: error message if user has insufficient permission level or
                 team edited unsuccessfully, otherwise return success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            command_team = param_list['team_name']
            team = get_team_by_name(self.facade, command_team)
            if not check_permissions(command_user, team):
                return self.permission_error, 200
            msg = f"Team edited: {command_team}, "
            if param_list['name'] is not None:
                msg += f"name: {param_list['name']}, "
                team.display_name = param_list['name']
            if param_list['platform'] is not None:
                msg += f"platform: {param_list['platform']}"
                team.platform = param_list['platform']
            if param_list['folder'] is not None:
                msg += f"folder: {param_list['folder']}"
                team.folder = param_list['folder']
            self.facade.store(team)
            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200
        except LookupError:
            return self.lookup_error, 200
Ejemplo n.º 4
0
    def team_lead(self,
                  caller_id: str,
                  lead_id: str,
                  gh_team_name: str,
                  remove: bool = False) -> bool:
        """
        Add a user as a team lead, and add them to team if not already added.

        :param caller_id: Slack ID of user who called command
        :param lead_id: Slack ID of user to declare as team lead
        :param gh_team_name: Github team name of team to add a lead to
        :param remove: if True, removes the user as team lead of the team
        :raises: LookupError if the calling user, the team to add a lead to
                 could not be found, the user is not on the team, or the user
                 is not a lead on the team
        :raises: RuntimeError if more than one team has the specified
                 team name
        :raises: PermissionError if the calling user does not have sufficient
                 permissions to add a lead to the specified team
        :returns: True if removal was successful, False otherwise
        """
        logging.info("Team lead command API called")
        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        team = db_utils.get_team_by_name(self._db_facade, gh_team_name)

        if not check_permissions(command_user, team):
            msg = f"User with ID {caller_id} has insufficient permissions" \
                f" to add lead to team {gh_team_name}"
            logging.error(msg)
            raise PermissionError(msg)

        lead_user = self._db_facade.retrieve(User, lead_id)
        logging.debug(f"User to add as lead: {lead_user.__str__()}")

        if remove:
            if not team.has_member(lead_user.github_id):
                msg = f"User with Github ID {lead_user.github_id} not a " \
                    "member of specified team"
                logging.error(msg)
                raise LookupError(msg)
            if team.has_team_lead(lead_user.github_id):
                team.discard_team_lead(lead_user.github_id)
                discarded = self._db_facade.store(team)
                return discarded
            else:
                msg = f"User with Github ID {lead_user.github_id} not a " \
                    "lead of specified team"
                logging.error(msg)
                raise LookupError(msg)
        else:
            if not team.has_member(lead_user.github_id):
                team.add_member(lead_user.github_id)
                self._gh_interface.add_team_member(lead_user.github_username,
                                                   team.github_team_id)
            team.add_team_lead(lead_user.github_id)
            added = self._db_facade.store(team)
            return added
Ejemplo n.º 5
0
 def test_check_credentials_not_lead(self):
     """Test checking to see if user is lead for certain team."""
     user = User("USFAS689")
     user.github_id = "IDGithub"
     team = Team("brussels", "team", "id")
     team.add_member(user.github_id)
     user.permissions_level = Permissions.team_lead
     self.assertFalse(util.check_permissions(user, team))
Ejemplo n.º 6
0
    def add_helper(self, args: Namespace, user_id: str) -> ResponseTuple:
        """
        Add user to team.

        If user is not admin or team lead of specified team, the user will not
        be added and an error message is returned.

        :param args: Parameters for adding user
        :param user_id: Slack ID of user who called command
        :return: error message if user added unsuccessfully or if user has
                 insufficient permission level, otherwise returns success
                 message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            command_team = args.team_name
            team = get_team_by_name(self.facade, command_team)
            if not check_permissions(command_user, team):
                return self.permission_error, 200

            user = self.facade.retrieve(User, args.username)
            if len(user.github_id) == 0:
                return self.no_ghusername_error, 200
            team.add_member(user.github_id)
            self.gh.add_team_member(user.github_username, team.github_team_id)
            self.facade.store(team)
            msg = "Added User to " + command_team

            # Update drive shares
            sync_team_email_perms(self.gcp, self.facade, team)

            # If this team is a team with special permissions, promote the
            # user to the appropriate permission
            promoted_level = Permissions.member
            if command_team == self.config.github_team_admin:
                promoted_level = Permissions.admin
            elif command_team == self.config.github_team_leads:
                promoted_level = Permissions.team_lead

            # Only perform promotion if it is actually a promotion.
            if promoted_level > user.permissions_level:
                logging.info(f"Promoting {command_user} to {promoted_level}")
                user.permissions_level = promoted_level
                self.facade.store(user)
                msg += f" and promoted user to {promoted_level}"
            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200

        except LookupError:
            return self.lookup_error, 200
        except GithubAPIException as e:
            logging.error("user added unsuccessfully to team")
            return f"User added unsuccessfully with the " \
                   f"following error: {e.data}", 200
Ejemplo n.º 7
0
    def team_remove(self, caller_id: str, gh_team_name: str,
                    rem_user_id: str) -> bool:
        """
        Remove the specified user from a team.

        If the user is also a team lead, removes team lead status from Team.

        :param caller_id: Slack ID of user who called command
        :param gh_team_name: Github team name of the team to remove user from
        :param rem_user_id: Slack ID of user to remove from team
        :raises: LookupError if the calling user, user to remove,
                 or specified team cannot be found in the database
        :raises: RuntimeError if more than one team has the specified
                 team name
        :raises: PermissionError if the calling user has insufficient
                 permission to remove members to the specified team
        :raises: GithubAPIException if an error occured removing the user from
                 the Github team
        :return: True if user was removed from team successfully,
                 False otherwise
        """
        logging.info("Team remove command API called")
        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        team = db_utils.get_team_by_name(self._db_facade, gh_team_name)

        if not check_permissions(command_user, team):
            msg = f"User with ID {caller_id} has insufficient permissions" \
                f" to remove members to team {gh_team_name}"
            logging.error(msg)
            raise PermissionError(msg)

        rem_user = self._db_facade.retrieve(User, rem_user_id)
        logging.debug(f"User to remove: {rem_user.__str__()}")

        if not self._gh_interface.has_team_member(rem_user.github_username,
                                                  team.github_team_id):
            msg = f"Github user {rem_user.github_username} not a member" \
                f" of Github team with ID {team.github_team_id}"
            logging.error(msg)
            raise GithubAPIException(msg)
        self._gh_interface.remove_team_member(rem_user.github_username,
                                              team.github_team_id)
        team.discard_member(rem_user.github_id)
        if team.has_team_lead(rem_user.github_id):
            team.discard_team_lead(rem_user.github_id)

        removed = self._db_facade.store(team)
        return removed
Ejemplo n.º 8
0
    def lead_helper(self, param_list, user_id) -> ResponseTuple:
        """
        Add a user as team lead, and add them to team if not already added.

        If ``--remove`` flag is used, user is instead demoted from being a team
        lead, but not from the team.

        :param param_list: List of parameters for editing leads
        :param user_id: Slack ID of user who called command
        :return: error message if user has insufficient permission level or
                 lead demoted unsuccessfully, otherwise return success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            teams = self.facade.query(
                Team, [('github_team_name', param_list['team_name'])])
            if len(teams) != 1:
                return self.lookup_error, 200
            team = teams[0]
            if not check_permissions(command_user, team):
                return self.permission_error, 200
            user = self.facade.retrieve(User, param_list["slack_id"])
            msg = ""
            if param_list["remove"]:
                if not team.has_member(user.github_id):
                    return "User not in team!", 200
                if team.has_team_lead(user.github_id):
                    team.discard_team_lead(user.github_id)
                self.facade.store(team)
                msg = f"User removed as team lead from" \
                      f" {param_list['team_name']}"
            else:
                if not team.has_member(user.github_id):
                    team.add_member(user.github_id)
                    self.gh.add_team_member(user.github_username,
                                            team.github_team_id)
                team.add_team_lead(user.github_id)
                self.facade.store(team)
                msg = f"User added as team lead to" \
                      f" {param_list['team_name']}"
            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200
        except LookupError:
            return self.lookup_error, 200
        except GithubAPIException as e:
            logging.error("team lead edit unsuccessful")
            return f"Edit team lead was unsuccessful " \
                   f"with the following error: {e.data}", 200
Ejemplo n.º 9
0
    def team_edit(self,
                  caller_id: str,
                  gh_team_name: str,
                  display_name: str = None,
                  platform: str = None) -> bool:
        """
        Edit the properties of a specific team.

        Team leads can only edit the teams that they are a part of, but admins
        can edit any team.

        :param caller_id: Slack ID of user who called command
        :param display_name: display name to change to if not None
        :param platform: platform to change to if not None
        :raises: LookupError if the calling user or team to edit
                 could not be found
        :raises: RuntimeError if more than one team has the specified
                 team name
        :raises: PermissionError if the calling user does not have sufficient
                 permissions to edit the specified team
        :return: True if the edit was successful, False otherwise
        """
        logging.info("Team edit command API called")
        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        team = db_utils.get_team_by_name(self._db_facade, gh_team_name)

        if not check_permissions(command_user, team):
            msg = f"User with ID {caller_id} has insufficient permissions" \
                f" to edit team {gh_team_name}"
            logging.error(msg)
            raise PermissionError(msg)

        if display_name is not None:
            logging.debug(f"Attaching display name {display_name} "
                          f"to {gh_team_name}")
            team.display_name = display_name

        if platform is not None:
            logging.debug(f"Attaching platform {platform} to {gh_team_name}")
            team.platform = platform

        edited = self._db_facade.store(team)
        return edited
Ejemplo n.º 10
0
    def remove_helper(self, param_list, user_id) -> ResponseTuple:
        """
        Remove specified user from a team.

        If the user is also a team lead, removes team lead status from Team. If
        user is not admin or team lead of specified team, user will not be
        removed and an error message is returned.

        :param param_list: List of parameters for removing user
        :param user_id: Slack ID of user who called command
        :return: error message if user removed unsuccessfully, if user is not
                 in team, or if user has insufficient permission level,
                 otherwise returns success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            teams = self.facade.query(
                Team, [('github_team_name', param_list['team_name'])])
            if len(teams) != 1:
                return self.lookup_error, 200
            team = teams[0]
            if not check_permissions(command_user, team):
                return self.permission_error, 200

            user = self.facade.retrieve(User, param_list['slack_id'])
            if not self.gh.has_team_member(user.github_username,
                                           team.github_team_id):
                return "User not in team!", 200
            team.discard_member(user.github_id)
            if team.has_team_lead(user.github_id):
                team.discard_team_lead(user.github_id)
            self.gh.remove_team_member(user.github_username,
                                       team.github_team_id)
            self.facade.store(team)
            msg = "Removed User from " + param_list['team_name']
            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200

        except LookupError:
            return self.lookup_error, 200
        except GithubAPIException as e:
            logging.error("user removed unsuccessfully from team")
            return f"User removed unsuccessfully with " \
                   f"the following error: {e.data}", 200
Ejemplo n.º 11
0
    def edit_helper(self, args: Namespace, user_id: str) -> ResponseTuple:
        """
        Edit the properties of a specific team.

        Team leads can only edit the teams that they are a part of, but admins
        can edit any teams.

        :param args: Parameters for editing team
        :param user_id: Slack ID of user who called command
        :return: error message if user has insufficient permission level or
                 team edited unsuccessfully, otherwise return success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            command_team = args.team_name
            team = get_team_by_name(self.facade, command_team)
            if not check_permissions(command_user, team):
                return self.permission_error, 200
            msg = f"Team edited: {command_team}, "
            if args.displayname is not None:
                msg += f"displayname: {args.displayname}, "
                team.displayname = args.displayname
            if args.platform is not None:
                msg += f"platform: {args.platform}"
                team.platform = args.platform
            if args.folder is not None:
                msg += f"folder: {args.folder}"
                team.folder = args.folder
            if args.github is not None:
                msg += f"new github team name: {args.github}"
                team.github_team_name = args.github
                self.gh.org_edit_team(
                    int(team.github_team_id), team.github_team_name)
            self.facade.store(team)

            # Update drive shares if folder was changed
            if args.folder:
                sync_team_email_perms(self.gcp, self.facade, team)

            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200
        except LookupError:
            return self.lookup_error, 200
Ejemplo n.º 12
0
    def team_add(self, caller_id: str, add_user_id: str,
                 gh_team_name: str) -> bool:
        """
        Add a user to a team.

        :param caller_id: Slack ID of user who called the API
        :param add_user_id: Slack ID of user to add to a team
        :param gh_team_name: Github team name of the team to add a user to
        :raises: LookupError if the calling user, user to add,
                 or specified team cannot be found in the database
        :raises: RuntimeError if more than one team has the specified
                 team name
        :raises: PermissionError if the calling user has insufficient
                 permission to add members to the specified team
        :raises: GithubAPIException if an error occurs when adding the user to
                 the Github team
        :return: True if adding the member to the team was successful,
                 False otherwise
        """
        logging.info("Team add command API called")
        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        team = db_utils.get_team_by_name(self._db_facade, gh_team_name)

        if not check_permissions(command_user, team):
            msg = f"User with ID {caller_id} has insufficient permissions" \
                f" to add members to team {gh_team_name}"
            logging.error(msg)
            raise PermissionError(msg)

        add_user = self._db_facade.retrieve(User, add_user_id)
        logging.debug(f"User to add: {add_user.__str__()}")

        self._gh_interface.add_team_member(add_user.github_username,
                                           team.github_team_id)
        team.add_member(add_user.github_id)

        added = self._db_facade.store(team)
        return added
Ejemplo n.º 13
0
    def add_helper(self, param_list, user_id) -> ResponseTuple:
        """
        Add user to team.

        If user is not admin or team lead of specified team, the user will not
        be added and an error message is returned.

        :param param_list: List of parameters for adding user
        :param user_id: Slack ID of user who called command
        :return: error message if user added unsuccessfully or if user has
                 insufficient permission level, otherwise returns success
                 message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            teams = self.facade.query(
                Team, [('github_team_name', param_list['team_name'])])
            if len(teams) != 1:
                return self.lookup_error, 200
            team = teams[0]
            if not check_permissions(command_user, team):
                return self.permission_error, 200

            user = self.facade.retrieve(User, param_list['slack_id'])
            team.add_member(user.github_id)
            self.gh.add_team_member(user.github_username, team.github_team_id)
            self.facade.store(team)
            msg = "Added User to " + param_list['team_name']
            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200

        except LookupError:
            return self.lookup_error, 200
        except GithubAPIException as e:
            logging.error("user added unsuccessfully to team")
            return f"User added unsuccessfully with the " \
                   f"following error: {e.data}", 200
Ejemplo n.º 14
0
    def handle(self, command: str, user_id: str) -> ResponseTuple:
        """Handle command by splitting into substrings and giving to parser."""
        logging.debug("Handling ExportCommand")
        command_arg = shlex.split(command)
        args = None

        try:
            args = self.parser.parse_args(command_arg)
        except SystemExit:
            all_subcommands = list(self.subparser.choices.keys())
            present_subcommands = [
                subcommand for subcommand in all_subcommands
                if subcommand in command_arg
            ]
            present_subcommand = None
            if len(present_subcommands) == 1:
                present_subcommand = present_subcommands[0]
            return self.get_help(subcommand=present_subcommand), 200

        if args.which == "emails":
            try:
                command_user = self.facade.retrieve(User, user_id)
                if not check_permissions(command_user, None):
                    return self.permission_error, 200

                # Check if team name is provided
                if args.team is not None:
                    users = self.get_team_users(args.team)
                    return self.export_emails_helper(users)
                else:  # if team name is not provided, export all emails
                    users = self.facade.query(User)
                    return self.export_emails_helper(users)
            except LookupError:
                return self.lookup_error, 200
        else:
            return self.get_help(), 200
Ejemplo n.º 15
0
    def refresh_helper(self, user_id) -> ResponseTuple:
        """
        Ensure that the local team database is the same as GitHub's.

        In the event that our local team database is outdated compared to
        the teams on GitHub, this command can be called to fix things.

        :return: error message if user has insufficient permission level
                 otherwise returns success messages with # of teams changed
        """
        num_changed = 0
        num_added = 0
        num_deleted = 0
        modified = []
        try:
            command_user = self.facade.retrieve(User, user_id)
            if not check_permissions(command_user, None):
                return self.permission_error, 200
            local_teams: List[Team] = self.facade.query(Team)
            remote_teams: List[Team] = self.gh.org_get_teams()
            local_team_dict = dict(
                (team.github_team_id, team) for team in local_teams)
            remote_team_dict = dict(
                (team.github_team_id, team) for team in remote_teams)

            # remove teams not in github anymore
            for local_id in local_team_dict:
                if local_id not in remote_team_dict:
                    self.facade.delete(Team, local_id)
                    num_deleted += 1
                    modified.append(local_team_dict[local_id].get_attachment())

            # add teams to db that are in github but not in local database
            for remote_id in remote_team_dict:
                if remote_id not in local_team_dict:
                    self.facade.store(remote_team_dict[remote_id])
                    num_added += 1
                    modified.append(
                        remote_team_dict[remote_id].get_attachment())
                else:
                    # and finally, if a local team differs, update it
                    old_team = local_team_dict[remote_id]
                    new_team = remote_team_dict[remote_id]
                    if old_team.github_team_name != new_team.github_team_name\
                            or old_team.members != new_team.members:

                        # update the old team, to retain additional parameters
                        old_team.github_team_name = new_team.github_team_name
                        old_team.members = new_team.members
                        self.facade.store(old_team)
                        num_changed += 1
                        modified.append(old_team.get_attachment())

            # add all members (if not already added) to the 'all' team
            self.refresh_all_team()

            # promote members inside special teams
            self.refresh_all_rocket_permissions()

            # enforce Drive permissions
            self.refresh_all_drive_permissions()
        except GithubAPIException as e:
            logging.error("team refresh unsuccessful due to github error")
            return "Refresh teams was unsuccessful with " \
                   f"the following error: {e.data}", 200
        except LookupError:
            logging.error("team refresh unsuccessful due to lookup error")
            return self.lookup_error, 200
        status = f"{num_changed} teams changed, " \
            f"{num_added} added, " \
            f"{num_deleted} deleted. Wonderful."
        ret = {'attachments': modified, 'text': status}
        return ret, 200
Ejemplo n.º 16
0
    def remove_helper(self, param_list, user_id) -> ResponseTuple:
        """
        Remove specified user from a team.

        If the user is also a team lead, removes team lead status from Team. If
        user is not admin or team lead of specified team, user will not be
        removed and an error message is returned.

        :param param_list: List of parameters for removing user
        :param user_id: Slack ID of user who called command
        :return: error message if user removed unsuccessfully, if user is not
                 in team, or if user has insufficient permission level,
                 otherwise returns success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            command_team = param_list['team_name']
            team = get_team_by_name(self.facade, command_team)
            if not check_permissions(command_user, team):
                return self.permission_error, 200

            user = self.facade.retrieve(User, param_list['username'])
            if not self.gh.has_team_member(user.github_username,
                                           team.github_team_id):
                return "User not in team!", 200
            team.discard_member(user.github_id)
            if team.has_team_lead(user.github_id):
                team.discard_team_lead(user.github_id)
            self.gh.remove_team_member(user.github_username,
                                       team.github_team_id)
            self.facade.store(team)

            msg = "Removed User from " + command_team

            # If the user is being removed from a team with special
            # permisisons, figure out a demotion strategy.
            demoted_level = None
            if command_team == self.config.github_team_leads:
                # If the user is currently an admin, we only demote this user
                # if it is currently NOT and admin team member
                if user.permissions_level == Permissions.admin \
                        and len(self.config.github_team_admin) > 0:
                    admins = get_team_by_name(self.facade,
                                              self.config.github_team_admin)
                    if not admins.has_member(user.github_id):
                        demoted_level = Permissions.member
                else:
                    demoted_level = Permissions.member
            if command_team == self.config.github_team_admin:
                # If the user is being removed from the admin team, we demote
                # this user to team_lead if this user is a member of the leads
                # team, otherwise we demote to member.
                demoted_level = Permissions.member
                if len(self.config.github_team_leads) > 0:
                    leads = get_team_by_name(self.facade,
                                             self.config.github_team_leads)
                    if leads.has_member(user.github_id):
                        demoted_level = Permissions.team_lead

            if demoted_level is not None:
                logging.info(f"Demoting {command_user} to member")
                user.permissions_level = demoted_level
                self.facade.store(user)
                msg += " and demoted user"
            ret = {'attachments': [team.get_attachment()], 'text': msg}
            return ret, 200

        except LookupError:
            return self.lookup_error, 200
        except GithubAPIException as e:
            logging.error("user removed unsuccessfully from team")
            return f"User removed unsuccessfully with " \
                   f"the following error: {e.data}", 200
Ejemplo n.º 17
0
    def team_refresh(self, caller_id: str) -> bool:
        """
        Ensure that the local team database is the same as Github's.

        In the event that our local team database is outdated compared to
        the teams on Github, this command can be called to fix things.

        :param caller_id: Slack ID of the user calling the command
        :raises: LookupError if the calling user cannot be found
        :raises: PermissionError if the calling user has insufficient
                 permissions to refresh the local database
        :raises: GithubAPIException if  there was a failure in fetching
                 Github team information
        :returns: True if synchronization was successful, False otherwise
        """
        logging.info("Team refresh command API called")
        num_changed = 0
        num_added = 0
        num_deleted = 0

        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        if not check_permissions(command_user, None):
            msg = f"User with ID {caller_id} has insufficient permissions" \
                " to refresh the local team database"
            logging.error(msg)
            raise PermissionError(msg)

        local_teams: List[Team] = self._db_facade.query(Team)
        remote_teams: List[Team] = self._gh_interface.org_get_teams()
        local_team_dict = dict(
            (team.github_team_id, team) for team in local_teams)
        remote_team_dict = dict(
            (team.github_team_id, team) for team in remote_teams)

        # remove teams not in github anymore
        for local_id in local_team_dict:
            if local_id not in remote_team_dict:
                self._db_facade.delete(Team, local_id)
                logging.debug(f"Team with Github ID {local_id} deleted")
                num_deleted += 1

        # add teams to db that are in github but not in local database
        for remote_id in remote_team_dict:
            remote_team = remote_team_dict[remote_id]
            if remote_id not in local_team_dict:
                stored = self._db_facade.store(remote_team)
                if stored:
                    logging.debug("Created new team with "
                                  f"Github ID {remote_id}")
                    num_added += 1
                else:
                    logging.error("Failed to create new team with "
                                  f"Github ID {remote_id}")
                    return False
            else:
                # and finally, if a local team differs, update it
                local_team = local_team_dict[remote_id]
                if local_team.github_team_name != \
                    remote_team.github_team_name \
                        or local_team.members != remote_team.members:
                    # update the old team, to retain additional parameters
                    local_team.github_team_name = remote_team.github_team_name
                    local_team.members = remote_team.members
                    edited = self._db_facade.store(local_team)
                    if edited:
                        logging.debug("Successfully edited team with "
                                      f"Github ID {remote_id}")
                        num_changed += 1
                    else:
                        logging.error("Failed to edit team with"
                                      f"Github ID {remote_id}")
                        return False

        logging.info(f"{num_changed} teams changed, {num_added} added, "
                     f"{num_deleted} deleted. Wonderful.")
        return True
Ejemplo n.º 18
0
    def create_helper(self, args: Namespace, user_id: str) -> ResponseTuple:
        """
        Create team and calls GitHub API to create the team in GitHub.

        If ``args.displayname is not None``, will add a display name. If
        ``args.channel is not None``, will add all members of channel in
        which the command was called into the team.
        :param args: Parameters for creating team
        :param user_id: Slack ID of user who called command
        :return: error message if team created unsuccessfully otherwise returns
                 success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            if not check_permissions(command_user, None):
                return self.permission_error, 200
            if not command_user.github_id:
                msg = f"User {command_user.slack_id} has yet to register a"\
                    f" Github username in this system."\
                    f" Register with `/rocket user edit --github username`."
                logging.error(msg)
                return msg, 200
            msg = f"New team created: {args.team_name}, "
            team_id = str(self.gh.org_create_team(args.team_name))
            team = Team(team_id, args.team_name, "")
            if args.displayname is not None:
                msg += f"displayname: {args.displayname}, "
                team.displayname = args.displayname
            if args.platform is not None:
                msg += f"platform: {args.platform}, "
                team.platform = args.platform
            if args.folder is not None:
                msg += f"folder: {args.folder}"
                team.folder = args.folder
            if args.channel is not None:
                msg += "added channel"
                channel_users = self.sc.get_channel_users(
                    args.channel)
                users_no_ghid = []
                for member_id in channel_users:
                    try:
                        member = self.facade.retrieve(User, member_id)
                        if not member.github_username:
                            users_no_ghid.append(member_id)
                            continue
                        self.gh.add_team_member(member.github_username,
                                                team_id)
                        team.add_member(member.github_id)
                    except (LookupError, GithubAPIException):
                        users_no_ghid.append(member_id)

                if users_no_ghid:
                    users_escaped = ' '.join(
                        [f'<@{uid}>' for uid in users_no_ghid])
                    no_gh_reminder =\
                        ' (users who forgot to set Github accounts or forgot '\
                        'to register into database: ' +\
                        users_escaped + ')'
                    msg += no_gh_reminder
                msg += ', '
            else:
                self.gh.add_team_member(command_user.github_username, team_id)
                team.add_member(command_user.github_id)
            if args.lead is not None:
                msg += "added lead"
                lead_user = self.facade.retrieve(User, args.lead)
                team.add_team_lead(lead_user.github_id)
                if not self.gh.has_team_member(lead_user.github_username,
                                               team_id):
                    self.gh.add_team_member(lead_user.github_username, team_id)
            else:
                team.add_team_lead(command_user.github_id)

            self.facade.store(team)
            return msg, 200
        except GithubAPIException as e:
            logging.error(f"Team creation error with {e.data}")
            return f"Team creation unsuccessful with the" \
                   f" following error: {e.data}", 200
        except LookupError:
            logging.error(f"User(uid={user_id}) isn't in database")
            return self.lookup_error, 200
        except SlackAPIError as e:
            logging.error(f"Slack error with {e.error}")
            return f"Team creation unsuccessful with the" \
                   f" following error: {e.error}", 200
Ejemplo n.º 19
0
 def test_check_credentials_admin(self):
     """Test checking to see if user is admin."""
     user = User("USFAS689")
     user.permissions_level = Permissions.admin
     self.assertTrue(util.check_permissions(user, None))
Ejemplo n.º 20
0
 def test_check_credentials_not_admin(self):
     """Test checking to see if user is not admin."""
     user = User("USFAS689")
     user.permissions_level = Permissions.member
     self.assertFalse(util.check_permissions(user, None))
Ejemplo n.º 21
0
    def team_create(self,
                    caller_id: str,
                    gh_team_name: str,
                    display_name: str = None,
                    platform: str = None,
                    channel: str = None,
                    lead_id: str = None) -> bool:
        """
        Create a team both in the Rocket database and Github organization.

        :param caller_id: Slack ID of the user who is calling the API
        :param gh_team_name: desired team name to give the team on Github
        :param display_name: display name to give the team when displayed
                             in various places
        :param platform: main platform this team's projects are based on
        :param channel: name of the Slack channel whose channel members
                        are to be added to the team - its members will be
                        added to the team in the Rocket database and added
                        to the Github team as well
        :param lead_id: Slack ID of the user who will be made the lead of
                        this team
        :raises: LookupError if the calling user or tech lead cannot be found
                 in the database
        :raises: PermissionError if the calling user has insufficient
                 permissions to create a team, or if the specified user with
                 lead_id does not have the permission to be a lead
        :raises: SlackAPIError if a channel name is provided by an error
                 is encountered retrieving the members of that channel
        :raises: GithubAPIException if an error occurs on team creation or
                 Github team member addition
        :raises: Exception for any other generic error
        :return: True if the team creation was successful, False otherwise
        """
        logging.info("Team create command API called")
        command_user = self._db_facade.retrieve(User, caller_id)
        logging.debug(f"Calling user: {command_user.__str__()}")

        if not check_permissions(command_user, None):
            msg = f"Calling user with Slack ID {caller_id} has permission" \
                f" level {str(command_user.permissions_level)}, " \
                "insufficient for creating a team!"
            logging.error(msg)
            raise PermissionError(msg)

        if not command_user.github_id:
            msg = f"User {command_user.slack_id} has yet to register a"\
                f" Github username in this system."\
                f" Register with `/rocket user edit --github username`."
            logging.error(msg)
            raise Exception(msg)

        gh_team_id = str(self._gh_interface.org_create_team(gh_team_name))
        logging.debug(f"Github team {gh_team_name} created with "
                      f"Github team ID {gh_team_id}")
        team = Team(gh_team_id, gh_team_name, "")

        if display_name is not None:
            logging.debug(f"Attaching display name {display_name} "
                          f"to {gh_team_name}")
            team.display_name = display_name

        if platform is not None:
            logging.debug(f"Attaching platform {platform} to {gh_team_name}")
            team.platform = platform

        if channel is not None:
            logging.debug(f"Adding channel members of #{channel} "
                          f"to {gh_team_name}")
            try:
                channel_member_ids = \
                    self._slack_client.get_channel_users(channel)
                logging.debug(f"Member IDs of members found in #{channel}: "
                              f"{channel_member_ids}")
            except SlackAPIError as e:
                msg = f"Channel member query on channel #{channel} failed: " \
                    f"{e.error}"
                logging.error(msg)
                raise SlackAPIError(msg)

            channel_members = \
                self._db_facade.bulk_retrieve(User,
                                              list(channel_member_ids.keys()))

            if len(channel_members) is not len(channel_member_ids):
                retrieved_members_ids = [
                    member.slack_id for member in channel_members
                ]
                unaccounted_member_ids = [
                    member_id for member_id in channel_member_ids
                    if member_id not in retrieved_members_ids
                ]
                logging.warning("Users not found for following Slack IDs: "
                                f"{unaccounted_member_ids}")

            for member in channel_members:
                self._gh_interface.add_team_member(member.github_username,
                                                   gh_team_id)
                team.add_member(member.github_id)
                logging.debug(f"Member with ID {member.slack_id} added "
                              f"to {gh_team_name}")
        else:
            self._gh_interface.add_team_member(command_user.github_username,
                                               gh_team_id)
            team.add_member(command_user.github_id)
            logging.debug(f"Calling user with ID {command_user.slack_id} "
                          f"added to {gh_team_name}")

        if lead_id is not None:
            lead = self._db_facade.retrieve(User, lead_id)

            if check_permissions(lead, None):
                lead_in_team = self._gh_interface.has_team_member(
                    lead.github_username, gh_team_id)
                if not lead_in_team:
                    self._gh_interface.add_team_member(lead.github_username,
                                                       gh_team_id)

                team.add_member(lead.github_id)
                team.add_team_lead(lead.github_id)
                logging.debug(f"User with ID {lead_id} set as tech lead of "
                              f"{gh_team_name}")
            else:
                msg = f"User specified with lead ID {lead_id} has" \
                    f" permission level {str(lead.permissions_level)}, " \
                    "insufficient to lead a team!"
                logging.error(msg)
                raise PermissionError(msg)
        else:
            team.add_team_lead(command_user.github_id)
            logging.debug(f"Calling user with ID {command_user.github_id} set"
                          f" as tech lead of {gh_team_name}")

        created = self._db_facade.store(team)
        return created
Ejemplo n.º 22
0
    def create_helper(self, param_list, user_id) -> ResponseTuple:
        """
        Create team and calls GitHub API to create the team in GitHub.

        If ``param_list[name] is not None``, will add a display name. If
        ``param_list[channel] is not None``, will add all members of channel in
        which the command was called into the team.

        :param param_list: List of parameters for creating team
        :param user_id: Slack ID of user who called command
        :return: error message if team created unsuccessfully otherwise returns
                 success message
        """
        try:
            command_user = self.facade.retrieve(User, user_id)
            if not check_permissions(command_user, None):
                return self.permission_error, 200
            if not command_user.github_id:
                msg = f"User {command_user.slack_id} has yet to register a"\
                    f" Github username in this system."\
                    f" Register with `/rocket user edit --github username`."
                logging.error(msg)
                return msg, 200
            msg = f"New team created: {param_list['team_name']}, "
            team_id = str(self.gh.org_create_team(param_list['team_name']))
            team = Team(team_id, param_list['team_name'], "")
            if param_list["name"] is not None:
                msg += f"name: {param_list['name']}, "
                team.display_name = param_list['name']
            if param_list["platform"] is not None:
                msg += f"platform: {param_list['platform']}, "
                team.platform = param_list['platform']
            if param_list["folder"] is not None:
                msg += f"folder: {param_list['folder']}"
                team.folder = param_list['folder']
            if param_list["channel"] is not None:
                msg += "added channel, "
                for member_id in self.sc.get_channel_users(
                        param_list["channel"]):
                    try:
                        member = self.facade.retrieve(User, member_id)
                        self.gh.add_team_member(member.github_username,
                                                team_id)
                        team.add_member(member.github_id)
                    except LookupError:
                        pass
            else:
                self.gh.add_team_member(command_user.github_username, team_id)
                team.add_member(command_user.github_id)
            if param_list["lead"] is not None:
                msg += "added lead"
                lead_user = self.facade.retrieve(User, param_list["lead"])
                team.add_team_lead(lead_user.github_id)
                if not self.gh.has_team_member(lead_user.github_username,
                                               team_id):
                    self.gh.add_team_member(lead_user.github_username, team_id)
            else:
                team.add_team_lead(command_user.github_id)

            self.facade.store(team)
            return msg, 200
        except GithubAPIException as e:
            logging.error(f"Team creation error with {e.data}")
            return f"Team creation unsuccessful with the" \
                   f" following error: {e.data}", 200
        except LookupError:
            logging.error(f"User(uid={user_id}) isn't in database")
            return self.lookup_error, 200
        except SlackAPIError as e:
            logging.error(f"Slack error with {e.error}")
            return f"Team creation unsuccessful with the" \
                   f" following error: {e.error}", 200