Delete an application member
This commit is contained in:
parent
547d813970
commit
27a4ef12c6
@ -3,6 +3,7 @@ from sqlalchemy.orm.exc import NoResultFound
|
|||||||
from atst.database import db
|
from atst.database import db
|
||||||
from . import BaseDomainClass
|
from . import BaseDomainClass
|
||||||
from atst.domain.application_roles import ApplicationRoles
|
from atst.domain.application_roles import ApplicationRoles
|
||||||
|
from atst.domain.environment_roles import EnvironmentRoles
|
||||||
from atst.domain.environments import Environments
|
from atst.domain.environments import Environments
|
||||||
from atst.domain.exceptions import NotFoundError
|
from atst.domain.exceptions import NotFoundError
|
||||||
from atst.domain.users import Users
|
from atst.domain.users import Users
|
||||||
@ -100,3 +101,15 @@ class Applications(BaseDomainClass):
|
|||||||
Environments.add_member(environment, user, env_role_data.get("role"))
|
Environments.add_member(environment, user, env_role_data.get("role"))
|
||||||
|
|
||||||
return application_role
|
return application_role
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_member(cls, application, user):
|
||||||
|
application_role = ApplicationRoles.get(
|
||||||
|
user_id=user.id, application_id=application.id
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.delete(application_role)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
for env in application.environments:
|
||||||
|
EnvironmentRoles.delete(user_id=user.id, environment_id=env.id)
|
||||||
|
@ -158,3 +158,31 @@ def create_member(application_id):
|
|||||||
_anchor="application-members",
|
_anchor="application-members",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@applications_bp.route(
|
||||||
|
"/applications/<application_id>/members/<user_id>/delete", methods=["POST"]
|
||||||
|
)
|
||||||
|
# TODO: Is this correct??
|
||||||
|
@user_can(Permissions.EDIT_APPLICATION_MEMBER, message="remove application member")
|
||||||
|
def remove_member(application_id, user_id):
|
||||||
|
application_role = ApplicationRoles.get(
|
||||||
|
application_id=application_id, user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
Applications.remove_member(application=g.application, user=application_role.user)
|
||||||
|
|
||||||
|
flash(
|
||||||
|
"application_member_removed",
|
||||||
|
user_name=application_role.user.full_name,
|
||||||
|
application_name=g.application.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"applications.team",
|
||||||
|
_anchor="application-members",
|
||||||
|
application_id=g.application.id,
|
||||||
|
fragment="application-members",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -2,6 +2,11 @@ from flask import flash, render_template_string
|
|||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
|
||||||
MESSAGES = {
|
MESSAGES = {
|
||||||
|
"application_member_removed": {
|
||||||
|
"title_template": "Team member removed from application",
|
||||||
|
"message_template": "You have successfully deleted {{ user_name }} from {{ application_name }}",
|
||||||
|
"category": "success",
|
||||||
|
},
|
||||||
"environment_deleted": {
|
"environment_deleted": {
|
||||||
"title_template": "{{ environment_name }} deleted",
|
"title_template": "{{ environment_name }} deleted",
|
||||||
"message_template": 'The environment "{{ environment_name }}" has been deleted',
|
"message_template": 'The environment "{{ environment_name }}" has been deleted',
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import ally from 'ally.js'
|
import ally from 'ally.js'
|
||||||
|
|
||||||
import checkboxinput from '../checkbox_input'
|
|
||||||
import DateSelector from '../date_selector'
|
import DateSelector from '../date_selector'
|
||||||
import FormMixin from '../../mixins/form'
|
import FormMixin from '../../mixins/form'
|
||||||
import levelofwarrant from '../levelofwarrant'
|
|
||||||
import Modal from '../../mixins/modal'
|
import Modal from '../../mixins/modal'
|
||||||
import multicheckboxinput from '../multi_checkbox_input'
|
|
||||||
import MultiStepModalForm from './multi_step_modal_form'
|
import MultiStepModalForm from './multi_step_modal_form'
|
||||||
|
import checkboxinput from '../checkbox_input'
|
||||||
|
import levelofwarrant from '../levelofwarrant'
|
||||||
|
import multicheckboxinput from '../multi_checkbox_input'
|
||||||
import optionsinput from '../options_input'
|
import optionsinput from '../options_input'
|
||||||
import textinput from '../text_input'
|
import textinput from '../text_input'
|
||||||
import toggler from '../toggler'
|
import toggler from '../toggler'
|
||||||
@ -14,12 +14,12 @@ import toggler from '../toggler'
|
|||||||
export default {
|
export default {
|
||||||
name: 'base-form',
|
name: 'base-form',
|
||||||
components: {
|
components: {
|
||||||
checkboxinput,
|
|
||||||
DateSelector,
|
DateSelector,
|
||||||
levelofwarrant,
|
|
||||||
Modal,
|
Modal,
|
||||||
multicheckboxinput,
|
|
||||||
MultiStepModalForm,
|
MultiStepModalForm,
|
||||||
|
checkboxinput,
|
||||||
|
levelofwarrant,
|
||||||
|
multicheckboxinput,
|
||||||
optionsinput,
|
optionsinput,
|
||||||
textinput,
|
textinput,
|
||||||
toggler,
|
toggler,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
{{ team_form.csrf_token }}
|
{{ team_form.csrf_token }}
|
||||||
|
|
||||||
{% for member_form in team_form.members %}
|
{% for member_form in team_form.members %}
|
||||||
|
{% set delete_modal_id = "delete-user-{}".format(member_form.id) %}
|
||||||
{% set environment_roles_form = member_form.environment_roles %}
|
{% set environment_roles_form = member_form.environment_roles %}
|
||||||
{% set permissions_form = member_form.permission_sets %}
|
{% set permissions_form = member_form.permission_sets %}
|
||||||
|
|
||||||
@ -38,6 +39,13 @@
|
|||||||
{{ environment_form.environment_name.data }}
|
{{ environment_form.environment_name.data }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% if user_can(permissions.EDIT_APPLICATION_MEMBER) %}
|
||||||
|
<li class="accordion-table__item__expanded action-group">
|
||||||
|
<span class="usa-button button-danger" v-on:click="openModal('{{ delete_modal_id }}')">
|
||||||
|
{{ "portfolios.members.archive_button" | translate }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{{ member_form.user_id() }}
|
{{ member_form.user_id() }}
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
{% from "components/toggle_list.html" import ToggleButton, ToggleSection %}
|
{% from "components/toggle_list.html" import ToggleButton, ToggleSection %}
|
||||||
{% from "components/multi_step_modal_form.html" import MultiStepModalForm %}
|
{% from "components/multi_step_modal_form.html" import MultiStepModalForm %}
|
||||||
{% import "fragments/applications/new_member_modal_content.html" as member_steps %}
|
{% import "fragments/applications/new_member_modal_content.html" as member_steps %}
|
||||||
|
{% from "components/alert.html" import Alert %}
|
||||||
|
{% from "components/delete_confirmation.html" import DeleteConfirmation %}
|
||||||
|
{% from "components/modal.html" import Modal %}
|
||||||
|
|
||||||
{% set secondary_breadcrumb = 'portfolios.applications.team_settings.title' | translate({ "application_name": application.name }) %}
|
{% set secondary_breadcrumb = 'portfolios.applications.team_settings.title' | translate({ "application_name": application.name }) %}
|
||||||
|
|
||||||
@ -109,6 +112,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</base-form>
|
</base-form>
|
||||||
|
|
||||||
|
{% if user_can(permissions.EDIT_APPLICATION_MEMBER) %}
|
||||||
|
{% for member_form in team_form.members %}
|
||||||
|
{% set delete_modal_id = "delete-user-{}".format(member_form.id) %}
|
||||||
|
{% call Modal(name=delete_modal_id, dismissable=True) %}
|
||||||
|
<h1>
|
||||||
|
{{ "portfolios.applications.remove_member.header" | translate }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{{
|
||||||
|
Alert(
|
||||||
|
title=("portfolios.applications.remove_member.alert.title" | translate),
|
||||||
|
message=("portfolios.applications.remove_member.alert.message" | translate({"user_name": member_form.user_name.data})),
|
||||||
|
level="warning"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{
|
||||||
|
DeleteConfirmation(
|
||||||
|
modal_id=delete_modal_id,
|
||||||
|
delete_text=('portfolios.applications.remove_member.button' | translate),
|
||||||
|
delete_action=url_for('applications.remove_member', application_id=application.id, user_id=member_form.data.user_id),
|
||||||
|
form=member_form
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{% endcall %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if user_can(permissions.CREATE_APPLICATION_MEMBER) %}
|
{% if user_can(permissions.CREATE_APPLICATION_MEMBER) %}
|
||||||
{% import "fragments/applications/new_member_modal_content.html" as member_steps %}
|
{% import "fragments/applications/new_member_modal_content.html" as member_steps %}
|
||||||
{{ MultiStepModalForm(
|
{{ MultiStepModalForm(
|
||||||
|
@ -2,16 +2,19 @@ import pytest
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from atst.models import CSPRole, ApplicationRoleStatus
|
from atst.models import CSPRole, ApplicationRoleStatus
|
||||||
|
from atst.domain.application_roles import ApplicationRoles
|
||||||
from atst.domain.applications import Applications
|
from atst.domain.applications import Applications
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.environment_roles import EnvironmentRoles
|
||||||
from atst.domain.exceptions import NotFoundError
|
from atst.domain.exceptions import NotFoundError
|
||||||
|
from atst.domain.permission_sets import PermissionSets
|
||||||
|
|
||||||
from tests.factories import (
|
from tests.factories import (
|
||||||
ApplicationFactory,
|
ApplicationFactory,
|
||||||
ApplicationRoleFactory,
|
ApplicationRoleFactory,
|
||||||
UserFactory,
|
|
||||||
PortfolioFactory,
|
|
||||||
EnvironmentFactory,
|
EnvironmentFactory,
|
||||||
|
EnvironmentRoleFactory,
|
||||||
|
PortfolioFactory,
|
||||||
|
UserFactory,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -155,3 +158,25 @@ def test_for_user():
|
|||||||
assert len(portfolio.applications) == 4
|
assert len(portfolio.applications) == 4
|
||||||
user_applications = Applications.for_user(user, portfolio)
|
user_applications = Applications.for_user(user, portfolio)
|
||||||
assert len(user_applications) == 2
|
assert len(user_applications) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_member():
|
||||||
|
application = ApplicationFactory.create()
|
||||||
|
user = UserFactory.create()
|
||||||
|
member_role = ApplicationRoleFactory.create(application=application, user=user)
|
||||||
|
environment = EnvironmentFactory.create(application=application)
|
||||||
|
environment_role = EnvironmentRoleFactory.create(user=user, environment=environment)
|
||||||
|
|
||||||
|
assert member_role == ApplicationRoles.get(
|
||||||
|
user_id=user.id, application_id=application.id
|
||||||
|
)
|
||||||
|
|
||||||
|
Applications.remove_member(application=application, user=member_role.user)
|
||||||
|
|
||||||
|
with pytest.raises(NotFoundError):
|
||||||
|
ApplicationRoles.get(user_id=user.id, application_id=application.id)
|
||||||
|
|
||||||
|
#
|
||||||
|
# TODO: Why does above raise NotFoundError and this returns None
|
||||||
|
#
|
||||||
|
assert EnvironmentRoles.get(user_id=user.id, environment_id=environment.id) == None
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import uuid
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
@ -128,3 +129,43 @@ def test_create_member(client, user_session):
|
|||||||
assert user.application_roles[0].application == application
|
assert user.application_roles[0].application == application
|
||||||
assert len(user.environment_roles) == 1
|
assert len(user.environment_roles) == 1
|
||||||
assert user.environment_roles[0].environment == env
|
assert user.environment_roles[0].environment == env
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_member_success(client, user_session):
|
||||||
|
user = UserFactory.create()
|
||||||
|
application = ApplicationFactory.create()
|
||||||
|
application_role = ApplicationRoleFactory.create(application=application, user=user)
|
||||||
|
|
||||||
|
user_session(application.portfolio.owner)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for(
|
||||||
|
"applications.remove_member", application_id=application.id, user_id=user.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == url_for(
|
||||||
|
"applications.team",
|
||||||
|
_anchor="application-members",
|
||||||
|
_external=True,
|
||||||
|
application_id=application.id,
|
||||||
|
fragment="application-members",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_member_failure(client, user_session):
|
||||||
|
user = UserFactory.create()
|
||||||
|
application = ApplicationFactory.create()
|
||||||
|
|
||||||
|
user_session(application.portfolio.owner)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for(
|
||||||
|
"applications.remove_member",
|
||||||
|
application_id=application.id,
|
||||||
|
user_id=uuid.uuid4(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
@ -416,6 +416,12 @@ portfolios:
|
|||||||
app_settings_text: App settings
|
app_settings_text: App settings
|
||||||
create_button_text: Create
|
create_button_text: Create
|
||||||
csp_console_text: CSP console
|
csp_console_text: CSP console
|
||||||
|
remove_member:
|
||||||
|
alert:
|
||||||
|
message: '{user_name} will no longer be able to access this application'
|
||||||
|
title: Warning! This action is permanent.
|
||||||
|
button: Remove member
|
||||||
|
header: Are you sure you want to remove this team member?
|
||||||
delete:
|
delete:
|
||||||
alert:
|
alert:
|
||||||
message: You will lose access to this application and all of its reporting and metrics tools. The activity log will be retained.
|
message: You will lose access to this application and all of its reporting and metrics tools. The activity log will be retained.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user