diff --git a/atst/domain/environment_roles.py b/atst/domain/environment_roles.py index 8b10a2db..5c0be864 100644 --- a/atst/domain/environment_roles.py +++ b/atst/domain/environment_roles.py @@ -14,3 +14,10 @@ class EnvironmentRoles(object): .one_or_none() ) return existing_env_role + + @classmethod + def delete(cls, user_id, environment_id): + existing_env_role = EnvironmentRoles.get(user_id, environment_id) + if existing_env_role: + db.session.delete(existing_env_role) + db.session.commit() diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 9555d12a..3b770ba9 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -58,10 +58,10 @@ class Environments(object): return env @classmethod - def update_environment_role(cls, user, ids_and_roles, workspace_user): + def update_environment_roles(cls, user, workspace, workspace_user, ids_and_roles): Authorization.check_workspace_permission( user, - workspace_user.workspace, + workspace, Permissions.ADD_AND_ASSIGN_CSP_ROLES, "assign environment roles", ) @@ -69,13 +69,19 @@ class Environments(object): for id_and_role in ids_and_roles: new_role = id_and_role["role"] environment = Environments.get(id_and_role["id"]) - env_role = EnvironmentRoles.get(workspace_user.user_id, id_and_role["id"]) - if env_role: - env_role.role = new_role + + if new_role is None: + EnvironmentRoles.delete(workspace_user.user.id, environment.id) else: - env_role = EnvironmentRole( - user=workspace_user.user, environment=environment, role=new_role + env_role = EnvironmentRoles.get( + workspace_user.user.id, id_and_role["id"] ) - db.session.add(env_role) + if env_role: + env_role.role = new_role + else: + env_role = EnvironmentRole( + user=workspace_user.user, environment=environment, role=new_role + ) + db.session.add(env_role) db.session.commit() diff --git a/atst/forms/data.py b/atst/forms/data.py index 3c48f316..d1577a86 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -153,6 +153,10 @@ ENVIRONMENT_ROLES = [ "description": "Views cloud resource usage and budget reports.", }, ), + ( + "", + {"name": "No Access", "description": "User has no access to this environment."}, + ), ] ENV_ROLE_MODAL_DESCRIPTION = { diff --git a/atst/models/workspace_user.py b/atst/models/workspace_user.py index 4e106455..e61182e4 100644 --- a/atst/models/workspace_user.py +++ b/atst/models/workspace_user.py @@ -56,6 +56,18 @@ class WorkspaceUser(object): .count() ) + @property + def environment_roles(self): + return ( + db.session.query(EnvironmentRole) + .join(EnvironmentRole.environment) + .join(Environment.project) + .join(Project.workspace) + .filter(Project.workspace_id == self.workspace_id) + .filter(EnvironmentRole.user_id == self.user_id) + .all() + ) + @property def has_environment_roles(self): return self.num_environment_roles > 0 diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index af59939b..dbea6810 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -285,12 +285,10 @@ def update_member(workspace_id, member_id): for entry in form_dict: if re.match("env_", entry): env_id = entry[4:] - env_role = form_dict[entry] - if env_role: - ids_and_roles.append({"id": env_id, "role": env_role}) + env_role = form_dict[entry] or None + ids_and_roles.append({"id": env_id, "role": env_role}) form = EditMemberForm(http_request.form) - if form.validate(): new_role_name = None if form.data["workspace_role"] != member.role: @@ -299,7 +297,9 @@ def update_member(workspace_id, member_id): ) new_role_name = member.role_displayname - Environments.update_environment_role(g.current_user, ids_and_roles, member) + Environments.update_environment_roles( + g.current_user, workspace, member, ids_and_roles + ) return redirect( url_for( diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_environment_role.js similarity index 68% rename from js/components/forms/edit_workspace_member.js rename to js/components/forms/edit_environment_role.js index 2a5c5e2a..45a3506a 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_environment_role.js @@ -4,8 +4,9 @@ import Selector from '../selector' import Modal from '../../mixins/modal' import toggler from '../toggler' + export default { - name: 'edit-workspace-member', + name: 'edit-environment-role', mixins: [FormMixin, Modal], @@ -19,6 +20,7 @@ export default { props: { choices: Array, initialData: String, + projectId: String }, data: function () { @@ -27,28 +29,37 @@ export default { } }, + mounted: function() { + this.$root.$on('revoke-' + this.projectId, this.revoke) + }, + methods: { change: function (e) { - e.preventDefault() this.new_role = e.target.value }, cancel: function () { this.new_role = this.initialData }, + revoke: function () { + this.new_role = "" + } }, computed: { displayName: function () { + const newRole = this.newRole for (var arr in this.choices) { - if (this.choices[arr][0] == this.new_role) { + if (this.choices[arr][0] == newRole) { return this.choices[arr][1].name } } - return this.new_role ? this.new_role : "no access" }, label_class: function () { - return this.displayName === "no access" ? + return this.newRole === "" ? "label" : "label label--success" }, - } + newRole: function () { + return this.new_role + } + }, } diff --git a/js/components/forms/edit_project_roles.js b/js/components/forms/edit_project_roles.js new file mode 100644 index 00000000..d1c5107c --- /dev/null +++ b/js/components/forms/edit_project_roles.js @@ -0,0 +1,26 @@ +import FormMixin from '../../mixins/form' +import Modal from '../../mixins/modal' +import toggler from '../toggler' +import EditEnvironmentRole from './edit_environment_role' + +export default { + name: 'edit-project-roles', + + mixins: [FormMixin, Modal], + + components: { + toggler, + EditEnvironmentRole, + }, + + props: { + name: String, + id: String + }, + + methods: { + doRevoke: function () { + this.$root.$emit('revoke-' + this.id) + } + } +} diff --git a/js/index.js b/js/index.js index 204f9871..a91f06c2 100644 --- a/js/index.js +++ b/js/index.js @@ -13,7 +13,8 @@ import poc from './components/forms/poc' import financial from './components/forms/financial' import toggler from './components/toggler' import NewProject from './components/forms/new_project' -import EditWorkspaceMember from './components/forms/edit_workspace_member' +import EditEnvironmentRole from './components/forms/edit_environment_role' +import EditProjectRoles from './components/forms/edit_project_roles' import Modal from './mixins/modal' import selector from './components/selector' import BudgetChart from './components/charts/budget_chart' @@ -41,7 +42,8 @@ const app = new Vue({ SpendTable, CcpoApproval, LocalDatetime, - EditWorkspaceMember, + EditEnvironmentRole, + EditProjectRoles, }, mounted: function() { diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index b1198791..0cec712b 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -51,89 +51,98 @@ {% for project in projects %} -
- +
+ {% endfor %}
diff --git a/tests/domain/test_environments.py b/tests/domain/test_environments.py index b81e0366..26ab0974 100644 --- a/tests/domain/test_environments.py +++ b/tests/domain/test_environments.py @@ -1,5 +1,6 @@ from atst.domain.environments import Environments from atst.domain.environment_roles import EnvironmentRoles +from atst.domain.workspace_users import WorkspaceUsers from tests.factories import UserFactory, WorkspaceFactory @@ -37,7 +38,9 @@ def test_update_environment_roles(): ] workspace_user = workspace.members[0] - Environments.update_environment_role(owner, new_ids_and_roles, workspace_user) + Environments.update_environment_roles( + owner, workspace, workspace_user, new_ids_and_roles + ) new_dev_env_role = EnvironmentRoles.get(workspace_user.user.id, dev_env.id) staging_env_role = EnvironmentRoles.get(workspace_user.user.id, staging_env.id) @@ -45,6 +48,57 @@ def test_update_environment_roles(): assert staging_env_role.role == "developer" +def test_remove_environment_role(): + owner = UserFactory.create() + developer = UserFactory.from_atat_role("developer") + workspace = WorkspaceFactory.create( + owner=owner, + members=[{"user": developer, "role_name": "developer"}], + projects=[ + { + "name": "project1", + "environments": [ + { + "name": "project1 dev", + "members": [{"user": developer, "role_name": "devops"}], + }, + { + "name": "project1 staging", + "members": [{"user": developer, "role_name": "developer"}], + }, + { + "name": "project1 uat", + "members": [ + {"user": developer, "role_name": "financial_auditor"} + ], + }, + {"name": "project1 prod"}, + ], + } + ], + ) + + project = workspace.projects[0] + now_ba = project.environments[0].id + now_none = project.environments[1].id + still_fa = project.environments[2].id + + new_environment_roles = [ + {"id": now_ba, "role": "billing_auditor"}, + {"id": now_none, "role": None}, + ] + + workspace_user = WorkspaceUsers.get(workspace.id, developer.id) + Environments.update_environment_roles( + owner, workspace, workspace_user, new_environment_roles + ) + + assert workspace_user.num_environment_roles == 2 + assert EnvironmentRoles.get(developer.id, now_ba).role == "billing_auditor" + assert EnvironmentRoles.get(developer.id, now_none) is None + assert EnvironmentRoles.get(developer.id, still_fa).role == "financial_auditor" + + def test_get_scoped_environments(db): developer = UserFactory.create() workspace = WorkspaceFactory.create(