diff --git a/atst/routes/applications/__init__.py b/atst/routes/applications/__init__.py index 8ec1e4cf..6a98e188 100644 --- a/atst/routes/applications/__init__.py +++ b/atst/routes/applications/__init__.py @@ -5,7 +5,6 @@ applications_bp = Blueprint("applications", __name__) from . import index from . import new from . import settings -from . import team from . import invitations from atst.domain.environment_roles import EnvironmentRoles from atst.domain.exceptions import UnauthorizedError diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index fa4616a8..a0bb30d8 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -1,8 +1,10 @@ -from flask import redirect, render_template, request as http_request, url_for +from flask import redirect, render_template, request as http_request, url_for, g from . import applications_bp +from atst.domain.exceptions import AlreadyExistsError from atst.domain.environments import Environments from atst.domain.applications import Applications +from atst.domain.application_roles import ApplicationRoles from atst.domain.audit_log import AuditLog from atst.domain.common import Paginator from atst.domain.environment_roles import EnvironmentRoles @@ -16,6 +18,8 @@ from atst.models.environment_role import CSPRole from atst.models.permissions import Permissions from atst.domain.permission_sets import PermissionSets from atst.utils.flash import formatted_flash as flash +from atst.utils.localization import translate +from atst.jobs import send_mail def get_environments_obj_for_app(application): @@ -161,6 +165,17 @@ def render_settings_page(application, **kwargs): ) +def send_application_invitation(invitee_email, inviter_name, token): + body = render_template( + "emails/application/invitation.txt", owner=inviter_name, token=token + ) + send_mail.delay( + [invitee_email], + translate("email.application_invite", {"inviter_name": inviter_name}), + body, + ) + + @applications_bp.route("/applications//settings") @user_can(Permissions.VIEW_APPLICATION, message="view application edit form") def settings(application_id): @@ -323,3 +338,110 @@ def delete_environment(environment_id): fragment="application-environments", ) ) + + +@applications_bp.route("/application//team", methods=["POST"]) +@user_can(Permissions.EDIT_APPLICATION_MEMBER, message="update application member") +def update_team(application_id): + application = Applications.get(application_id) + form = TeamForm(http_request.form) + + if form.validate(): + for member_form in form.members: + app_role = ApplicationRoles.get_by_id(member_form.role_id.data) + new_perms = [ + perm + for perm in member_form.data["permission_sets"] + if perm != PermissionSets.VIEW_APPLICATION + ] + ApplicationRoles.update_permission_sets(app_role, new_perms) + + for environment_role_form in member_form.environment_roles: + environment = Environments.get( + environment_role_form.environment_id.data + ) + Environments.update_env_role( + environment, app_role, environment_role_form.data.get("role") + ) + + flash("updated_application_team_settings", application_name=application.name) + + return redirect( + url_for( + "applications.settings", + application_id=application_id, + fragment="application-members", + _anchor="application-members", + ) + ) + else: + return (render_settings_page(application), 400) + + +@applications_bp.route("/application//members/new", methods=["POST"]) +@user_can( + Permissions.CREATE_APPLICATION_MEMBER, message="create new application member" +) +def create_member(application_id): + application = Applications.get(application_id) + form = NewMemberForm(http_request.form) + + if form.validate(): + try: + invite = Applications.invite( + application=application, + inviter=g.current_user, + user_data=form.user_data.data, + permission_sets_names=form.permission_sets.data, + environment_roles_data=form.environment_roles.data, + ) + + send_application_invitation( + invitee_email=invite.email, + inviter_name=g.current_user.full_name, + token=invite.token, + ) + + flash("new_application_member", user_name=invite.user_name) + + except AlreadyExistsError: + return render_template( + "error.html", message="There was an error processing your request." + ) + else: + pass + # TODO: flash error message + + return redirect( + url_for( + "applications.settings", + application_id=application_id, + fragment="application-members", + _anchor="application-members", + ) + ) + + +@applications_bp.route( + "/applications//members//delete", + methods=["POST"], +) +@user_can(Permissions.DELETE_APPLICATION_MEMBER, message="remove application member") +def remove_member(application_id, application_role_id): + application_role = ApplicationRoles.get_by_id(application_role_id) + Applications.remove_member(application_role) + + flash( + "application_member_removed", + user_name=application_role.user_name, + application_name=g.application.name, + ) + + return redirect( + url_for( + "applications.settings", + _anchor="application-members", + application_id=g.application.id, + fragment="application-members", + ) + ) diff --git a/atst/routes/applications/team.py b/atst/routes/applications/team.py deleted file mode 100644 index 03c489c3..00000000 --- a/atst/routes/applications/team.py +++ /dev/null @@ -1,198 +0,0 @@ -from flask import render_template, request as http_request, g, url_for, redirect - - -from . import applications_bp -from atst.domain.applications import Applications -from atst.domain.application_roles import ApplicationRoles -from atst.domain.authz.decorator import user_can_access_decorator as user_can -from atst.domain.environment_roles import EnvironmentRoles -from atst.domain.environments import Environments -from atst.domain.exceptions import AlreadyExistsError -from atst.domain.permission_sets import PermissionSets -from atst.forms.application_member import NewForm as NewMemberForm -from atst.forms.team import TeamForm -from atst.models import Permissions -from atst.utils.flash import formatted_flash as flash -from atst.utils.localization import translate -from atst.jobs import send_mail - - -def get_form_permission_value(member, edit_perm_set): - if member.has_permission_set(edit_perm_set): - return edit_perm_set - else: - return PermissionSets.VIEW_APPLICATION - - -def get_team_form(application): - team_data = [] - for member in application.members: - permission_sets = { - "perms_team_mgmt": get_form_permission_value( - member, PermissionSets.EDIT_APPLICATION_TEAM - ), - "perms_env_mgmt": get_form_permission_value( - member, PermissionSets.EDIT_APPLICATION_ENVIRONMENTS - ), - "perms_del_env": get_form_permission_value( - member, PermissionSets.DELETE_APPLICATION_ENVIRONMENTS - ), - } - roles = EnvironmentRoles.get_for_application_member(member.id) - environment_roles = [ - { - "environment_id": str(role.environment.id), - "environment_name": role.environment.name, - "role": role.role, - } - for role in roles - ] - team_data.append( - { - "role_id": member.id, - "user_name": member.user_name, - "permission_sets": permission_sets, - "environment_roles": environment_roles, - } - ) - - return TeamForm(data={"members": team_data}) - - -def get_new_member_form(application): - env_roles = [ - {"environment_id": e.id, "environment_name": e.name} - for e in application.environments - ] - - return NewMemberForm(data={"environment_roles": env_roles}) - - -def render_team_page(application): - team_form = get_team_form(application) - new_member_form = get_new_member_form(application) - - return render_template( - "portfolios/applications/team.html", - application=application, - team_form=team_form, - new_member_form=new_member_form, - ) - - -@applications_bp.route("/application//team", methods=["POST"]) -@user_can(Permissions.EDIT_APPLICATION_MEMBER, message="update application member") -def update_team(application_id): - application = Applications.get(application_id) - form = TeamForm(http_request.form) - - if form.validate(): - for member_form in form.members: - app_role = ApplicationRoles.get_by_id(member_form.role_id.data) - new_perms = [ - perm - for perm in member_form.data["permission_sets"] - if perm != PermissionSets.VIEW_APPLICATION - ] - ApplicationRoles.update_permission_sets(app_role, new_perms) - - for environment_role_form in member_form.environment_roles: - environment = Environments.get( - environment_role_form.environment_id.data - ) - Environments.update_env_role( - environment, app_role, environment_role_form.data.get("role") - ) - - flash("updated_application_team_settings", application_name=application.name) - - return redirect( - url_for( - "applications.settings", - application_id=application_id, - fragment="application-members", - _anchor="application-members", - ) - ) - else: - return (render_team_page(application), 400) - - -def send_application_invitation(invitee_email, inviter_name, token): - body = render_template( - "emails/application/invitation.txt", owner=inviter_name, token=token - ) - send_mail.delay( - [invitee_email], - translate("email.application_invite", {"inviter_name": inviter_name}), - body, - ) - - -@applications_bp.route("/application//members/new", methods=["POST"]) -@user_can( - Permissions.CREATE_APPLICATION_MEMBER, message="create new application member" -) -def create_member(application_id): - application = Applications.get(application_id) - form = NewMemberForm(http_request.form) - - if form.validate(): - try: - invite = Applications.invite( - application=application, - inviter=g.current_user, - user_data=form.user_data.data, - permission_sets_names=form.permission_sets.data, - environment_roles_data=form.environment_roles.data, - ) - - send_application_invitation( - invitee_email=invite.email, - inviter_name=g.current_user.full_name, - token=invite.token, - ) - - flash("new_application_member", user_name=invite.user_name) - - except AlreadyExistsError: - return render_template( - "error.html", message="There was an error processing your request." - ) - else: - pass - # TODO: flash error message - - return redirect( - url_for( - "applications.settings", - application_id=application_id, - fragment="application-members", - _anchor="application-members", - ) - ) - - -@applications_bp.route( - "/applications//members//delete", - methods=["POST"], -) -@user_can(Permissions.DELETE_APPLICATION_MEMBER, message="remove application member") -def remove_member(application_id, application_role_id): - application_role = ApplicationRoles.get_by_id(application_role_id) - Applications.remove_member(application_role) - - flash( - "application_member_removed", - user_name=application_role.user_name, - application_name=g.application.name, - ) - - return redirect( - url_for( - "applications.settings", - _anchor="application-members", - application_id=g.application.id, - fragment="application-members", - ) - ) diff --git a/templates/portfolios/applications/team.html b/templates/portfolios/applications/team.html deleted file mode 100644 index ac71a3f8..00000000 --- a/templates/portfolios/applications/team.html +++ /dev/null @@ -1,154 +0,0 @@ -{% extends "portfolios/applications/base.html" %} - -{% from "components/icon.html" import Icon %} -{% from "components/multi_step_modal_form.html" import MultiStepModalForm %} -{% from 'components/save_button.html' import SaveButton %} -{% 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 }) %} - -{% block application_content %} - {% if not application.members %} - {% set user_can_invite = user_can(permissions.CREATE_APPLICATION_MEMBER) %} - -
-

{{ ("portfolios.applications.team_settings.blank_slate.title" | translate) }}

- - {{ Icon('avatar') }} - - {% if not user_can_invite %} -

{{ ("portfolios.applications.team_settings.blank_slate.sub_message" | translate) }}

- {% endif %} - - {% if user_can_invite %} - {% set new_member_modal_name = "add-app-mem" %} - - {{ "portfolios.applications.team_settings.blank_slate.action_label" | translate }} - - {{ MultiStepModalForm( - name=new_member_modal_name, - form=new_member_form, - form_action=url_for("applications.create_member", application_id=application.id), - steps=[ - member_steps.MemberStepOne(new_member_form), - member_steps.MemberStepTwo(new_member_form, application) - ], - ) }} - {% endif %} -
- - {% else %} -
- {{ 'portfolios.applications.team_settings.subheading' | translate }} -
- -
- -
-
- {% if g.matchesPath("application-members") %} - {% include "fragments/flash.html" %} - {% endif %} -
-
-
-
- {{ "portfolios.applications.team_settings.section.title" | translate({ "application_name": application.name }) }} -

Members ({{ team_form.members | length }})

-
-
-
-
- -
-
-
- {{ "common.name" | translate }} -
-
- {{ "portfolios.applications.team_settings.section.table.team_management" | translate }} -
-
- {{ "portfolios.applications.team_settings.section.table.environment_management" | translate }} -
-
- {{ "portfolios.applications.team_settings.section.table.delete_access" | translate }} -
-
-   -
-
-
    - {% if user_can(permissions.EDIT_APPLICATION_MEMBER) %} - {% include "fragments/applications/edit_team.html" %} - {% elif user_can(permissions.VIEW_APPLICATION_MEMBER) %} - {% include "fragments/applications/read_only_team.html" %} - {% endif %} -
-
- - -
-
-
- - {% if user_can(permissions.DELETE_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) %} -

- {{ "portfolios.applications.remove_member.header" | translate }} -

- - {{ - Alert( - title=("components.modal.destructive_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, application_role_id=member_form.data.role_id), - form=member_form - ) - }} - {% endcall %} - {% endfor %} - {% endif %} - - {% if user_can(permissions.CREATE_APPLICATION_MEMBER) %} - {% import "fragments/applications/new_member_modal_content.html" as member_steps %} - {{ MultiStepModalForm( - name=new_member_modal_name, - form=new_member_form, - form_action=url_for("applications.create_member", application_id=application.id), - steps=[ - member_steps.MemberStepOne(new_member_form), - member_steps.MemberStepTwo(new_member_form, application) - ], - ) }} - {% endif %} -
- {% endif %} -{% endblock %} diff --git a/tests/routes/applications/test_settings.py b/tests/routes/applications/test_settings.py index 1e184048..7adc525e 100644 --- a/tests/routes/applications/test_settings.py +++ b/tests/routes/applications/test_settings.py @@ -1,5 +1,7 @@ import pytest +import uuid from flask import url_for, get_flashed_messages +from unittest.mock import Mock from tests.factories import * @@ -424,3 +426,252 @@ def test_delete_environment(client, user_session): assert environment.name in message["message"] # deletes environment assert len(application.environments) == 0 + + +def test_update_team_permissions(client, user_session): + application = ApplicationFactory.create() + owner = application.portfolio.owner + app_role = ApplicationRoleFactory.create( + application=application, permission_sets=[] + ) + user_session(owner) + response = client.post( + url_for("applications.update_team", application_id=application.id), + data={ + "members-0-role_id": app_role.id, + "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, + "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, + "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, + }, + ) + + assert response.status_code == 302 + actual_perms_names = [perm.name for perm in app_role.permission_sets] + expected_perms_names = [ + PermissionSets.VIEW_APPLICATION, + PermissionSets.EDIT_APPLICATION_TEAM, + PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, + PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, + ] + assert expected_perms_names == actual_perms_names + + +def test_update_team_with_bad_permission_sets(client, user_session): + application = ApplicationFactory.create() + owner = application.portfolio.owner + app_role = ApplicationRoleFactory.create( + application=application, permission_sets=[] + ) + permission_sets = app_role.permission_sets + + user_session(owner) + response = client.post( + url_for("applications.update_team", application_id=application.id), + data={ + "members-0-role_id": app_role.id, + "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, + "members-0-permission_sets-perms_env_mgmt": "some random string", + }, + ) + assert response.status_code == 400 + assert app_role.permission_sets == permission_sets + + +def test_update_team_with_non_app_user(client, user_session): + application = ApplicationFactory.create() + owner = application.portfolio.owner + + user_session(owner) + response = client.post( + url_for("applications.update_team", application_id=application.id), + data={ + "members-0-role_id": str(uuid.uuid4()), + "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, + "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, + "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, + }, + ) + + assert response.status_code == 404 + + +def test_update_team_environment_roles(client, user_session): + application = ApplicationFactory.create() + owner = application.portfolio.owner + app_role = ApplicationRoleFactory.create( + application=application, permission_sets=[] + ) + environment = EnvironmentFactory.create(application=application) + env_role = EnvironmentRoleFactory.create( + application_role=app_role, + environment=environment, + role=CSPRole.NETWORK_ADMIN.value, + ) + user_session(owner) + response = client.post( + url_for("applications.update_team", application_id=application.id), + data={ + "members-0-role_id": app_role.id, + "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, + "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, + "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, + "members-0-environment_roles-0-environment_id": environment.id, + "members-0-environment_roles-0-role": CSPRole.TECHNICAL_READ.value, + }, + ) + + assert response.status_code == 302 + assert env_role.role == CSPRole.TECHNICAL_READ.value + + +def test_update_team_revoke_environment_access(client, user_session, db, session): + application = ApplicationFactory.create() + owner = application.portfolio.owner + user = UserFactory.create() + app_role = ApplicationRoleFactory.create( + application=application, user=user, permission_sets=[] + ) + environment = EnvironmentFactory.create(application=application) + env_role = EnvironmentRoleFactory.create( + application_role=app_role, + environment=environment, + role=CSPRole.BASIC_ACCESS.value, + ) + assert user in environment.users + + user_session(owner) + response = client.post( + url_for("applications.update_team", application_id=application.id), + data={ + "members-0-role_id": app_role.id, + "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, + "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, + "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, + "members-0-environment_roles-0-environment_id": environment.id, + "members-0-environment_roles-0-role": NO_ACCESS, + }, + ) + + assert response.status_code == 302 + env_role_exists = db.exists().where(EnvironmentRole.id == env_role.id) + assert not session.query(env_role_exists).scalar() + assert user not in environment.users + + +def test_create_member(monkeypatch, client, user_session, session): + job_mock = Mock() + monkeypatch.setattr("atst.jobs.send_mail.delay", job_mock) + user = UserFactory.create() + application = ApplicationFactory.create( + environments=[{"name": "Naboo"}, {"name": "Endor"}] + ) + env = application.environments[0] + env_1 = application.environments[1] + + user_session(application.portfolio.owner) + + response = client.post( + url_for("applications.create_member", application_id=application.id), + data={ + "user_data-first_name": user.first_name, + "user_data-last_name": user.last_name, + "user_data-dod_id": user.dod_id, + "user_data-email": user.email, + "environment_roles-0-environment_id": env.id, + "environment_roles-0-role": "Basic Access", + "environment_roles-0-environment_name": env.name, + "environment_roles-1-environment_id": env_1.id, + "environment_roles-1-role": NO_ACCESS, + "environment_roles-1-environment_name": env_1.name, + "permission_sets-perms_env_mgmt": True, + "permission_sets-perms_team_mgmt": True, + "permission_sets-perms_del_env": True, + }, + ) + + assert response.status_code == 302 + expected_url = url_for( + "applications.settings", + application_id=application.id, + fragment="application-members", + _anchor="application-members", + _external=True, + ) + assert response.location == expected_url + assert len(application.roles) == 1 + environment_roles = application.roles[0].environment_roles + assert len(environment_roles) == 1 + assert environment_roles[0].environment == env + + invitation = ( + session.query(ApplicationInvitation).filter_by(dod_id=user.dod_id).one() + ) + assert invitation.role.application == application + + assert job_mock.called + + +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, + application_role_id=application_role.id, + ) + ) + + assert response.status_code == 302 + assert response.location == url_for( + "applications.settings", + _anchor="application-members", + _external=True, + application_id=application.id, + fragment="application-members", + ) + + +def test_remove_new_member_success(client, user_session): + application = ApplicationFactory.create() + application_role = ApplicationRoleFactory.create(application=application, user=None) + + user_session(application.portfolio.owner) + + response = client.post( + url_for( + "applications.remove_member", + application_id=application.id, + application_role_id=application_role.id, + ) + ) + + assert response.status_code == 302 + assert response.location == url_for( + "applications.settings", + _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, + application_role_id=uuid.uuid4(), + ) + ) + + assert response.status_code == 404 diff --git a/tests/routes/applications/test_team.py b/tests/routes/applications/test_team.py deleted file mode 100644 index 79d5e5d0..00000000 --- a/tests/routes/applications/test_team.py +++ /dev/null @@ -1,259 +0,0 @@ -import uuid -from unittest.mock import Mock - -from flask import url_for - -from atst.domain.permission_sets import PermissionSets -from atst.models import CSPRole -from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS - -from tests.factories import * - - -def test_update_team_permissions(client, user_session): - application = ApplicationFactory.create() - owner = application.portfolio.owner - app_role = ApplicationRoleFactory.create( - application=application, permission_sets=[] - ) - user_session(owner) - response = client.post( - url_for("applications.update_team", application_id=application.id), - data={ - "members-0-role_id": app_role.id, - "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, - "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, - "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, - }, - ) - - assert response.status_code == 302 - actual_perms_names = [perm.name for perm in app_role.permission_sets] - expected_perms_names = [ - PermissionSets.VIEW_APPLICATION, - PermissionSets.EDIT_APPLICATION_TEAM, - PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, - PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, - ] - assert expected_perms_names == actual_perms_names - - -def test_update_team_with_bad_permission_sets(client, user_session): - application = ApplicationFactory.create() - owner = application.portfolio.owner - app_role = ApplicationRoleFactory.create( - application=application, permission_sets=[] - ) - permission_sets = app_role.permission_sets - - user_session(owner) - response = client.post( - url_for("applications.update_team", application_id=application.id), - data={ - "members-0-role_id": app_role.id, - "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, - "members-0-permission_sets-perms_env_mgmt": "some random string", - }, - ) - assert response.status_code == 400 - assert app_role.permission_sets == permission_sets - - -def test_update_team_with_non_app_user(client, user_session): - application = ApplicationFactory.create() - owner = application.portfolio.owner - - user_session(owner) - response = client.post( - url_for("applications.update_team", application_id=application.id), - data={ - "members-0-role_id": str(uuid.uuid4()), - "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, - "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, - "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, - }, - ) - - assert response.status_code == 404 - - -def test_update_team_environment_roles(client, user_session): - application = ApplicationFactory.create() - owner = application.portfolio.owner - app_role = ApplicationRoleFactory.create( - application=application, permission_sets=[] - ) - environment = EnvironmentFactory.create(application=application) - env_role = EnvironmentRoleFactory.create( - application_role=app_role, - environment=environment, - role=CSPRole.NETWORK_ADMIN.value, - ) - user_session(owner) - response = client.post( - url_for("applications.update_team", application_id=application.id), - data={ - "members-0-role_id": app_role.id, - "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, - "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, - "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, - "members-0-environment_roles-0-environment_id": environment.id, - "members-0-environment_roles-0-role": CSPRole.TECHNICAL_READ.value, - }, - ) - - assert response.status_code == 302 - assert env_role.role == CSPRole.TECHNICAL_READ.value - - -def test_update_team_revoke_environment_access(client, user_session, db, session): - application = ApplicationFactory.create() - owner = application.portfolio.owner - user = UserFactory.create() - app_role = ApplicationRoleFactory.create( - application=application, user=user, permission_sets=[] - ) - environment = EnvironmentFactory.create(application=application) - env_role = EnvironmentRoleFactory.create( - application_role=app_role, - environment=environment, - role=CSPRole.BASIC_ACCESS.value, - ) - assert user in environment.users - - user_session(owner) - response = client.post( - url_for("applications.update_team", application_id=application.id), - data={ - "members-0-role_id": app_role.id, - "members-0-permission_sets-perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM, - "members-0-permission_sets-perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, - "members-0-permission_sets-perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, - "members-0-environment_roles-0-environment_id": environment.id, - "members-0-environment_roles-0-role": NO_ACCESS, - }, - ) - - assert response.status_code == 302 - env_role_exists = db.exists().where(EnvironmentRole.id == env_role.id) - assert not session.query(env_role_exists).scalar() - assert user not in environment.users - - -def test_create_member(monkeypatch, client, user_session, session): - job_mock = Mock() - monkeypatch.setattr("atst.jobs.send_mail.delay", job_mock) - user = UserFactory.create() - application = ApplicationFactory.create( - environments=[{"name": "Naboo"}, {"name": "Endor"}] - ) - env = application.environments[0] - env_1 = application.environments[1] - - user_session(application.portfolio.owner) - - response = client.post( - url_for("applications.create_member", application_id=application.id), - data={ - "user_data-first_name": user.first_name, - "user_data-last_name": user.last_name, - "user_data-dod_id": user.dod_id, - "user_data-email": user.email, - "environment_roles-0-environment_id": env.id, - "environment_roles-0-role": "Basic Access", - "environment_roles-0-environment_name": env.name, - "environment_roles-1-environment_id": env_1.id, - "environment_roles-1-role": NO_ACCESS, - "environment_roles-1-environment_name": env_1.name, - "permission_sets-perms_env_mgmt": True, - "permission_sets-perms_team_mgmt": True, - "permission_sets-perms_del_env": True, - }, - ) - - assert response.status_code == 302 - expected_url = url_for( - "applications.settings", - application_id=application.id, - fragment="application-members", - _anchor="application-members", - _external=True, - ) - assert response.location == expected_url - assert len(application.roles) == 1 - environment_roles = application.roles[0].environment_roles - assert len(environment_roles) == 1 - assert environment_roles[0].environment == env - - invitation = ( - session.query(ApplicationInvitation).filter_by(dod_id=user.dod_id).one() - ) - assert invitation.role.application == application - - assert job_mock.called - - -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, - application_role_id=application_role.id, - ) - ) - - assert response.status_code == 302 - assert response.location == url_for( - "applications.settings", - _anchor="application-members", - _external=True, - application_id=application.id, - fragment="application-members", - ) - - -def test_remove_new_member_success(client, user_session): - application = ApplicationFactory.create() - application_role = ApplicationRoleFactory.create(application=application, user=None) - - user_session(application.portfolio.owner) - - response = client.post( - url_for( - "applications.remove_member", - application_id=application.id, - application_role_id=application_role.id, - ) - ) - - assert response.status_code == 302 - assert response.location == url_for( - "applications.settings", - _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, - application_role_id=uuid.uuid4(), - ) - ) - - assert response.status_code == 404