Merge pull request #394 from dod-ccpo/revoke-member-access
Revoke members' environment access
This commit is contained in:
commit
e9588221e7
@ -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()
|
||||
|
@ -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,7 +69,13 @@ 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 new_role is None:
|
||||
EnvironmentRoles.delete(workspace_user.user.id, environment.id)
|
||||
else:
|
||||
env_role = EnvironmentRoles.get(
|
||||
workspace_user.user.id, id_and_role["id"]
|
||||
)
|
||||
if env_role:
|
||||
env_role.role = new_role
|
||||
else:
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
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(
|
||||
|
@ -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
|
||||
}
|
||||
},
|
||||
}
|
26
js/components/forms/edit_project_roles.js
Normal file
26
js/components/forms/edit_project_roles.js
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -51,6 +51,8 @@
|
||||
</div>
|
||||
|
||||
{% for project in projects %}
|
||||
{% set revoke_modal_name = project.name + 'RevokeModal' %}
|
||||
<edit-project-roles inline-template v-bind:name="'{{ project.name }}'" v-bind:id="'{{ project.id }}'">
|
||||
<div is='toggler' default-visible class='block-list project-list-item'>
|
||||
<template slot-scope='props'>
|
||||
<header class='block-list__header'>
|
||||
@ -59,15 +61,27 @@
|
||||
<template v-else>{{ Icon('caret_right') }}</template>
|
||||
<h3 class="block-list__title">{{ project.name }}</h3>
|
||||
</button>
|
||||
<span><a href="#" class="icon-link icon-link--danger">revoke all access</a></span>
|
||||
<span><a v-on:click="openModal('{{ revoke_modal_name }}')" class="icon-link icon-link--danger">revoke all access</a></span>
|
||||
</header>
|
||||
{% call Modal(name=revoke_modal_name, dismissable=False) %}
|
||||
<div>
|
||||
<h1>Revoke Access</h1>
|
||||
<p>
|
||||
Confirming will revoke access for {{ member.user.full_name }} to any environments associated with {{ project.name }}.
|
||||
</p>
|
||||
<div class='action-group'>
|
||||
<a v-on:click="doRevoke(); closeModal('{{ revoke_modal_name }}')" class='action-group__action usa-button'>Confirm</a>
|
||||
<a class='action-group__action icon-link icon-link--danger' v-on:click="closeModal('{{ revoke_modal_name }}'); cancel();">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
<ul v-show='props.isVisible'>
|
||||
{% for env in project.environments %}
|
||||
|
||||
{% set role = EnvironmentRoles.get(member.user_id, env.id).role %}
|
||||
|
||||
<li class='block-list__item'>
|
||||
<edit-workspace-member inline-template initial-data='{{ role }}' v-bind:choices='{{ choices | tojson }}'>
|
||||
<edit-environment-role inline-template initial-data='{{ role or "" }}' v-bind:choices='{{ choices | tojson }}' v-bind:project-id="'{{ project.id }}'">
|
||||
<div class='project-list-item__environment'>
|
||||
<span class='project-list-item__environment__link'>
|
||||
{{ env.name }}
|
||||
@ -89,18 +103,13 @@
|
||||
<ul>
|
||||
{% for choice in choices %}
|
||||
<li class='block-list__item block-list__item--selectable'>
|
||||
|
||||
{% if choice[0] != "" %}
|
||||
<input
|
||||
name='radio_input_{{ env.id }}'
|
||||
v-on:change='change'
|
||||
v-on:change.prevent='change'
|
||||
type='radio'
|
||||
id="env_{{ env.id }}_{{ choice[0] }}"
|
||||
value='{{ choice[0] }}'
|
||||
{% if role == choice[0] %}
|
||||
checked='checked'
|
||||
autofocus
|
||||
{% endif %}
|
||||
:checked="new_role === '{{ choice[0]}}'"
|
||||
/>
|
||||
<label for="env_{{ env.id }}_{{ choice[0] }}">
|
||||
{% if choice[1].description %}
|
||||
@ -112,28 +121,28 @@
|
||||
{{ choice[1].name }}
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<input type='hidden' name='env_{{ env.id }}' v-bind:value='new_role'/>
|
||||
<input type='hidden' name='env_{{ env.id }}' v-bind:value='newRole'/>
|
||||
<div class='block-list__footer'>
|
||||
<div class='action-group'>
|
||||
<button type='button' v-on:click="closeModal('{{ env.name }}RolesModal')" class='action-group__action usa-button'>Select Access Role</button>
|
||||
<button type='button' class='action-group__action icon-link icon-link--danger' v-on:click="closeModal('{{ env.name }}RolesModal'); cancel();" value="{{ value if value == role else role }}" >Cancel</button>
|
||||
<a v-on:click="closeModal('{{ env.name }}RolesModal')" class='action-group__action usa-button'>Select Access Role</a>
|
||||
<a class='action-group__action icon-link icon-link--danger' v-on:click="closeModal('{{ env.name }}RolesModal'); cancel();" value="{{ value if value == role else role }}" >Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
</edit-workspace-member>
|
||||
</edit-environment-role>
|
||||
</li>
|
||||
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</template>
|
||||
</div>
|
||||
</edit-project-roles>
|
||||
{% endfor %}
|
||||
|
||||
<div class='action-group'>
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user