From edd1bbca697bc9dc5f6c4f046b6043044aeae2a5 Mon Sep 17 00:00:00 2001 From: Robert Lin Date: Mon, 28 Sep 2020 15:09:45 +0800 Subject: [PATCH] feat: add ability to set permissions from teams --- app/controller/command/commands/team.py | 34 +++++++++++++-- config/__init__.py | 6 +++ docs/Config.rst | 23 ++++++++++ sample-env | 3 ++ .../controller/command/commands/team_test.py | 42 ++++++++++++++++++- 5 files changed, 102 insertions(+), 6 deletions(-) diff --git a/app/controller/command/commands/team.py b/app/controller/command/commands/team.py index eb131a17..61bfbdec 100644 --- a/app/controller/command/commands/team.py +++ b/app/controller/command/commands/team.py @@ -4,6 +4,7 @@ from argparse import ArgumentParser, _SubParsersAction from app.controller import ResponseTuple from app.controller.command.commands.base import Command +from app.model.permissions import Permissions from db.facade import DBFacade from db.utils import get_team_by_name from interface.github import GithubAPIException, GithubInterface @@ -385,8 +386,9 @@ def add_helper(self, param_list, user_id) -> ResponseTuple: """ try: command_user = self.facade.retrieve(User, user_id) + command_team = param_list['team_name'] teams = self.facade.query(Team, [('github_team_name', - param_list['team_name'])]) + command_team)]) if len(teams) != 1: return self.lookup_error, 200 team = teams[0] @@ -397,7 +399,20 @@ def add_helper(self, param_list, user_id) -> ResponseTuple: 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'] + + # If this team is a team with special permissions, promote the user + 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 + + msg = "Added User to " + command_team + if promoted_level != Permissions.member: + 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 @@ -424,8 +439,9 @@ def remove_helper(self, param_list, user_id) -> ResponseTuple: """ try: command_user = self.facade.retrieve(User, user_id) + command_team = param_list['team_name'] teams = self.facade.query(Team, [('github_team_name', - param_list['team_name'])]) + command_team)]) if len(teams) != 1: return self.lookup_error, 200 team = teams[0] @@ -442,7 +458,17 @@ def remove_helper(self, param_list, user_id) -> ResponseTuple: self.gh.remove_team_member(user.github_username, team.github_team_id) self.facade.store(team) - msg = "Removed User from " + param_list['team_name'] + + msg = "Removed User from " + command_team + + # If this team is a team with special permissions, demote the user + if command_team == self.config.github_team_admin\ + or command_team == self.config.github_team_leads: + logging.info(f"Demoting {command_user} to member") + user.permissions_level = Permissions.member + self.facade.store(user) + msg += " and demoted user" + ret = {'attachments': [team.get_attachment()], 'text': msg} return ret, 200 diff --git a/config/__init__.py b/config/__init__.py index baf500a2..58879a8e 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -21,6 +21,8 @@ class Config: 'GITHUB_APP_ID': 'github_app_id', 'GITHUB_ORG_NAME': 'github_org_name', 'GITHUB_DEFAULT_TEAM_NAME': 'github_team_all', + 'GITHUB_ADMIN_TEAM_NAME': 'github_team_admin', + 'GITHUB_LEADS_TEAM_NAME': 'github_team_leads', 'GITHUB_WEBHOOK_ENDPT': 'github_webhook_endpt', 'GITHUB_WEBHOOK_SECRET': 'github_webhook_secret', 'GITHUB_KEY': 'github_key', @@ -39,6 +41,8 @@ class Config: OPTIONALS = { 'AWS_LOCAL': 'False', 'GITHUB_DEFAULT_TEAM_NAME': 'all', + 'GITHUB_ADMIN_TEAM_NAME': '', + 'GITHUB_LEADS_TEAM_NAME': '', 'GCP_SERVICE_ACCOUNT_CREDENTIALS': '', 'GCP_SERVICE_ACCOUNT_SUBJECT': '', } @@ -84,6 +88,8 @@ def _set_attrs(self): self.github_app_id = '' self.github_org_name = '' self.github_team_all = '' + self.github_team_admin = '' + self.github_team_leads = '' self.github_webhook_endpt = '' self.github_webhook_secret = '' self.github_key = '' diff --git a/docs/Config.rst b/docs/Config.rst index 97318f2e..9d7f751d 100644 --- a/docs/Config.rst +++ b/docs/Config.rst @@ -72,6 +72,29 @@ GITHUB_ORG_NAME The name of your Github organization (the string in the URL whenever you go to the organization. +GITHUB_DEFAULT_TEAM_NAME +------------------------ + +The name of the GitHub team in your organization that all users should +be added to. Optional, defaults to ``all``. + +GITHUB_ADMIN_TEAM_NAME +---------------------- + +The name of the GitHub team in your organization that should be automatically +promoted to Rocket administrators. Optional. + +Note that this does not mean all Rocket administrators will be added to this +team. + +GITHUB_LEADS_TEAM_NAME +---------------------- + +The name of the GitHub team in your organization that should be automatically +promoted to Rocket team leads. Optional. + +Note that this does not mean all Rocket team leads will be added to this team. + GITHUB_WEBHOOK_ENDPT -------------------- diff --git a/sample-env b/sample-env index 4453bc97..334ef7bc 100644 --- a/sample-env +++ b/sample-env @@ -7,6 +7,9 @@ SLACK_API_TOKEN='' GITHUB_APP_ID='' GITHUB_ORG_NAME='ubclaunchpad' +GITHUB_DEFAULT_TEAM_NAME='all' +GITHUB_ADMIN_TEAM_NAME='exec' +GITHUB_LEADS_TEAM_NAME='leads' GITHUB_WEBHOOK_ENDPT='/webhook' GITHUB_WEBHOOK_SECRET='' GITHUB_KEY='BEGIN KEY END KEY' diff --git a/tests/app/controller/command/commands/team_test.py b/tests/app/controller/command/commands/team_test.py index 3615f202..3ba4fedd 100644 --- a/tests/app/controller/command/commands/team_test.py +++ b/tests/app/controller/command/commands/team_test.py @@ -18,9 +18,10 @@ def setUp(self): self.admin = create_test_admin('Uadmin') self.t0 = Team("BRS", "brs", "web") self.t1 = Team("OTEAM", "other team", "android") + self.t2 = Team("LEADS", "leads", "") self.db = MemoryDB( users=[self.u0, self.u1, self.admin], - teams=[self.t0, self.t1]) + teams=[self.t0, self.t1, self.t2]) self.sc = mock.MagicMock() self.testcommand = TeamCommand(self.config, self.db, self.gh, self.sc) @@ -28,6 +29,7 @@ def setUp(self): self.maxDiff = None self.config.github_team_all = 'all' + self.config.github_team_leads = 'leads' def test_get_help(self): subcommands = list(self.testcommand.subparser.choices.keys()) @@ -70,7 +72,8 @@ def test_handle_subcommand_help(self): def test_handle_list(self): attachment = [ self.t0.get_basic_attachment(), - self.t1.get_basic_attachment() + self.t1.get_basic_attachment(), + self.t2.get_basic_attachment(), ] with self.app.app_context(): resp, code = self.testcommand.handle('team list', self.u0.slack_id) @@ -237,6 +240,23 @@ def test_handle_add_lookup_error(self): (self.testcommand.lookup_error, 200)) self.gh.add_team_member.assert_not_called() + def test_handle_add_promote(self): + self.u0.github_username = 'myuser' + self.u0.github_id = 'otherID' + self.t2.github_team_id = 'githubid' + with self.app.app_context(): + resp, code = self.testcommand.handle( + f'team add leads {self.u0.slack_id}', + self.admin.slack_id) + expect_msg = 'Added User to leads and promoted user to team_lead' + expect = {'attachments': [self.t2.get_attachment()], + 'text': expect_msg} + self.assertDictEqual(resp, expect) + self.assertEqual(code, 200) + self.assertTrue(self.t2.has_member("otherID")) + self.assertEquals(self.u0.permissions_level, Permissions.team_lead) + self.gh.add_team_member.assert_called_once_with('myuser', 'githubid') + def test_handle_remove(self): self.u0.github_id = 'githubID' self.u0.github_username = 'myuser' @@ -268,6 +288,24 @@ def test_handle_remove_user_not_in_team(self): self.t0.github_team_id) self.gh.remove_team_member.assert_not_called() + def test_handle_remove_demote(self): + self.u0.github_username = 'myuser' + self.u0.github_id = 'otherID' + self.t2.add_member(self.u0.github_id) + with self.app.app_context(): + resp, code = self.testcommand.handle( + f'team remove leads {self.u0.slack_id}', + self.admin.slack_id) + expect_msg = 'Removed User from leads and demoted user' + expect = {'attachments': [self.t2.get_attachment()], + 'text': expect_msg} + self.assertDictEqual(resp, expect) + self.assertEqual(code, 200) + self.assertEquals(self.u0.permissions_level, Permissions.member) + self.gh.remove_team_member.assert_called_once_with( + self.u0.github_username, + self.t2.github_team_id) + def test_handle_remove_not_admin(self): with self.app.app_context(): self.assertTupleEqual(self.testcommand.handle(