Merge pull request #1146 from dod-ccpo/app-members-perms-form

Add revoke access to app members perms/env roles form
This commit is contained in:
leigh-mil 2019-11-13 11:12:16 -05:00 committed by GitHub
commit 92ce3420b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 334 additions and 97 deletions

View File

@ -29,6 +29,7 @@ class EnvironmentRoles(object):
EnvironmentRole.application_role_id == application_role_id, EnvironmentRole.application_role_id == application_role_id,
EnvironmentRole.environment_id == environment_id, EnvironmentRole.environment_id == environment_id,
EnvironmentRole.deleted == False, EnvironmentRole.deleted == False,
EnvironmentRole.status != EnvironmentRole.Status.DISABLED,
) )
.one_or_none() .one_or_none()
) )
@ -57,6 +58,14 @@ class EnvironmentRoles(object):
) )
return existing_env_role return existing_env_role
@classmethod
def _update_status(cls, environment_role, new_status):
environment_role.status = new_status
db.session.add(environment_role)
db.session.commit()
return environment_role
@classmethod @classmethod
def delete(cls, application_role_id, environment_id): def delete(cls, application_role_id, environment_id):
existing_env_role = EnvironmentRoles.get(application_role_id, environment_id) existing_env_role = EnvironmentRoles.get(application_role_id, environment_id)
@ -104,3 +113,15 @@ class EnvironmentRoles(object):
db.session.commit() db.session.commit()
return environment_role return environment_role
@classmethod
def get_for_update(cls, application_role_id, environment_id):
existing_env_role = (
db.session.query(EnvironmentRole)
.filter(
EnvironmentRole.application_role_id == application_role_id,
EnvironmentRole.environment_id == environment_id,
)
.one_or_none()
)
return existing_env_role

View File

@ -4,10 +4,17 @@ from typing import List
from uuid import UUID from uuid import UUID
from atst.database import db from atst.database import db
from atst.models import Environment, Application, Portfolio, TaskOrder, CLIN from atst.models import (
Environment,
Application,
Portfolio,
TaskOrder,
CLIN,
EnvironmentRole,
)
from atst.domain.environment_roles import EnvironmentRoles from atst.domain.environment_roles import EnvironmentRoles
from .exceptions import NotFoundError from .exceptions import NotFoundError, DisabledError
class Environments(object): class Environments(object):
@ -50,29 +57,31 @@ class Environments(object):
@classmethod @classmethod
def update_env_role(cls, environment, application_role, new_role): def update_env_role(cls, environment, application_role, new_role):
updated = False env_role = EnvironmentRoles.get_for_update(application_role.id, environment.id)
if env_role and (
env_role.status == EnvironmentRole.Status.DISABLED or env_role.deleted
):
raise DisabledError("environment_role", env_role.id)
if new_role is None: if (
updated = EnvironmentRoles.delete(application_role.id, environment.id) env_role
else: and env_role.role != new_role
env_role = EnvironmentRoles.get(application_role.id, environment.id) and env_role.status != EnvironmentRole.Status.DISABLED
if env_role and env_role.role != new_role: ):
env_role.role = new_role env_role.role = new_role
updated = True db.session.add(env_role)
db.session.add(env_role) elif not env_role and new_role:
elif not env_role: env_role = EnvironmentRoles.create(
env_role = EnvironmentRoles.create( application_role=application_role,
application_role=application_role, environment=environment,
environment=environment, role=new_role,
role=new_role, )
) db.session.add(env_role)
updated = True
db.session.add(env_role)
if updated: if env_role and not new_role:
db.session.commit() EnvironmentRoles.disable(env_role.id)
return updated db.session.commit()
@classmethod @classmethod
def revoke_access(cls, environment, target_user): def revoke_access(cls, environment, target_user):

View File

@ -53,3 +53,13 @@ class ClaimFailedException(Exception):
f"Could not acquire claim for {resource.__class__.__name__} {resource.id}." f"Could not acquire claim for {resource.__class__.__name__} {resource.id}."
) )
super().__init__(message) super().__init__(message)
class DisabledError(Exception):
def __init__(self, resource_name, resource_id=None):
self.resource_name = resource_name
self.resource_id = resource_id
@property
def message(self):
return f"Cannot update disabled {self.resource_name} {self.resource_id}."

View File

@ -16,8 +16,9 @@ class EnvironmentForm(Form):
environment_name, environment_name,
choices=ENV_ROLES, choices=ENV_ROLES,
default=NO_ACCESS, default=NO_ACCESS,
filters=[lambda x: None if x == "None" else x], filters=[lambda x: NO_ACCESS if x == "None" else x],
) )
disabled = BooleanField("Revoke Access", default=False)
@property @property
def data(self): def data(self):

View File

@ -1,5 +1,5 @@
from enum import Enum from enum import Enum
from sqlalchemy import and_, Index, ForeignKey, Column, Enum as SQLAEnum, Table from sqlalchemy import Index, ForeignKey, Column, Enum as SQLAEnum, Table
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.event import listen from sqlalchemy.event import listen
@ -8,7 +8,6 @@ from atst.utils import first_or_none
from atst.models.base import Base from atst.models.base import Base
import atst.models.mixins as mixins import atst.models.mixins as mixins
import atst.models.types as types import atst.models.types as types
from atst.models.environment_role import EnvironmentRole
from atst.models.mixins.auditable import record_permission_sets_updates from atst.models.mixins.auditable import record_permission_sets_updates
@ -55,9 +54,7 @@ class ApplicationRole(
environment_roles = relationship( environment_roles = relationship(
"EnvironmentRole", "EnvironmentRole",
primaryjoin=and_( primaryjoin="and_(EnvironmentRole.application_role_id == ApplicationRole.id, EnvironmentRole.deleted == False)",
EnvironmentRole.application_role_id == id, EnvironmentRole.deleted == False
),
) )
@property @property

View File

@ -14,6 +14,7 @@ from atst.forms.application import NameAndDescriptionForm, EditEnvironmentForm
from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS
from atst.forms.member import NewForm as MemberForm from atst.forms.member import NewForm as MemberForm
from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.models.environment_role import EnvironmentRole
from atst.models.permissions import Permissions from atst.models.permissions import Permissions
from atst.domain.permission_sets import PermissionSets from atst.domain.permission_sets import PermissionSets
from atst.utils.flash import formatted_flash as flash from atst.utils.flash import formatted_flash as flash
@ -31,7 +32,14 @@ def get_environments_obj_for_app(application):
"edit_form": EditEnvironmentForm(obj=env), "edit_form": EditEnvironmentForm(obj=env),
"member_count": len(env.roles), "member_count": len(env.roles),
"members": sorted( "members": sorted(
[env_role.application_role.user_name for env_role in env.roles] [
{
"user_name": env_role.application_role.user_name,
"status": env_role.status.value,
}
for env_role in env.roles
],
key=lambda env_role: env_role["user_name"],
), ),
} }
for env in application.environments for env in application.environments
@ -77,11 +85,14 @@ def filter_env_roles_form_data(member, environments):
"environment_id": str(env.id), "environment_id": str(env.id),
"environment_name": env.name, "environment_name": env.name,
"role": NO_ACCESS, "role": NO_ACCESS,
"disabled": False,
} }
env_roles_set = set(env.roles).intersection(set(member.environment_roles)) env_roles_set = set(env.roles).intersection(set(member.environment_roles))
if len(env_roles_set) == 1: if len(env_roles_set) == 1:
(env_role,) = env_roles_set (env_role,) = env_roles_set
env_data["role"] = env_role.role env_data["role"] = env_role.role
env_data["disabled"] = env_role.status == EnvironmentRole.Status.DISABLED
env_roles_form_data.append(env_data) env_roles_form_data.append(env_data)
@ -373,14 +384,21 @@ def remove_member(application_id, application_role_id):
@user_can(Permissions.EDIT_APPLICATION_MEMBER, message="update application member") @user_can(Permissions.EDIT_APPLICATION_MEMBER, message="update application member")
def update_member(application_id, application_role_id): def update_member(application_id, application_role_id):
app_role = ApplicationRoles.get_by_id(application_role_id) app_role = ApplicationRoles.get_by_id(application_role_id)
form = UpdateMemberForm(http_request.form) application = Applications.get(application_id)
existing_env_roles_data = filter_env_roles_form_data(
app_role, application.environments
)
form = UpdateMemberForm(
formdata=http_request.form, environment_roles=existing_env_roles_data
)
if form.validate(): if form.validate():
ApplicationRoles.update_permission_sets(app_role, form.data["permission_sets"]) ApplicationRoles.update_permission_sets(app_role, form.data["permission_sets"])
for env_role in form.environment_roles: for env_role in form.environment_roles:
environment = Environments.get(env_role.environment_id.data) environment = Environments.get(env_role.environment_id.data)
Environments.update_env_role(environment, app_role, env_role.data["role"]) new_role = None if env_role.disabled.data else env_role.data["role"]
Environments.update_env_role(environment, app_role, new_role)
flash("application_member_updated", user_name=app_role.user_name) flash("application_member_updated", user_name=app_role.user_name)
else: else:

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M16 16h-2v-3.225l-3.919-.781C9.455 11.869 9 11.314 9 10.675V9.242c0-.477.236-.921.631-1.187C9.919 7.86 11 6.595 11 5c0-1.853-1.558-3-3-3-1.449 0-3 1.206-3 3 0 1.596 1.081 2.859 1.371 3.056.395.268.629.711.629 1.186v1.433c0 .64-.455 1.194-1.083 1.319l-3.916.783L2 16H0v-3.221c0-.951.677-1.776 1.609-1.963L5 10.139v-.623C4.235 8.839 3 7.136 3 5c0-3.088 2.595-5 5-5 2.757 0 5 2.243 5 5 0 2.134-1.234 3.837-2 4.516v.623l3.396.679c.929.187 1.604 1.01 1.604 1.957V16z"/></svg> <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user" class="svg-inline--fa fa-user fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z"></path></svg>

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 484 B

View File

@ -91,16 +91,52 @@
margin-bottom: 8 * $gap; margin-bottom: 8 * $gap;
text-align: left; text-align: left;
.usa-input { .form-row {
margin: $gap 0 $gap 0; margin: $gap * 2;
}
.usa-input__title-inline { .usa-alert {
margin-top: $gap; margin: -0.5em 0;
margin-left: $gap; padding: $gap;
background-image: unset;
&-body {
padding: 0;
width: 100%;
display: unset;
}
}
.usa-input {
&__title-inline {
font-weight: $font-bold;
} }
.form-row { &__help {
margin: 0; margin-bottom: 0;
}
&.env-role__no-access {
.usa-input__title-inline,
.usa-input__help {
color: $color-gray;
}
}
&__choices {
&.revoke-button label {
padding: 1rem 2rem;
&::before {
display: none;
}
&.link {
color: $color-blue;
text-decoration: underline;
text-align: right;
}
}
} }
} }
@ -112,15 +148,69 @@
margin: 0.5em 0; margin: 0.5em 0;
} }
} }
.form-row {
margin: 0;
}
} }
.environment-name--gray { .environment-role {
font-weight: $font-normal; padding: ($gap * 2) 0;
color: $color-gray-medium;
h4 {
margin-bottom: $gap / 4;
}
&__users {
background-color: $color-gray-lightest;
padding: ($gap * 1.2) ($gap * 0.6);
font-size: $small-font-size;
display: flex;
flex-wrap: wrap;
.environment-role__user {
background-color: $color-white;
border-radius: 0.5rem;
padding: ($gap / 2) $gap;
border: solid 2px $color-blue;
margin: $gap;
white-space: nowrap;
width: 20rem;
position: relative;
height: 3.6rem;
&-field {
position: absolute;
background-color: $color-white;
margin-top: $gap * 2;
padding: $gap;
left: -0.1rem;
border: solid 1px $color-gray-light;
width: 20rem;
z-index: 3;
.usa-input {
margin: 0;
li {
background-color: $color-white;
border: none;
}
}
}
&.unassigned {
border: solid 1px $color-gray-light;
}
.icon-link {
padding: 0;
}
}
.environment-role__no-user {
margin: $gap;
padding: ($gap / 2) $gap;
font-weight: $font-normal;
height: 3.6rem;
}
}
} }
.application-list-item { .application-list-item {

View File

@ -64,8 +64,9 @@
{% call ToggleSection(section_name="members") %} {% call ToggleSection(section_name="members") %}
<ul> <ul>
{% for member in env['members'] %} {% for member in env['members'] %}
{% set status = ": Access Suspended" if member['status'] == 'disabled' %}
<li class="accordion-table__item-toggle-content__expanded"> <li class="accordion-table__item-toggle-content__expanded">
{{ member }} {{ member['user_name'] }}{{ status }}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -1,7 +1,88 @@
{% from "components/alert.html" import Alert %}
{% from "components/checkbox_input.html" import CheckboxInput %} {% from "components/checkbox_input.html" import CheckboxInput %}
{% from "components/text_input.html" import TextInput %} {% from "components/text_input.html" import TextInput %}
{% from "components/phone_input.html" import PhoneInput %} {% from "components/phone_input.html" import PhoneInput %}
{% macro EnvRoleInput(sub_form, member_role_id=None) %}
{% set role = sub_form.role.data if not sub_form.disabled.data else "Access Suspended" %}
{% if sub_form.role.data != "No Access" and not sub_form.disabled.data -%}
<checkboxinput
name="'{{ sub_form.disabled.name | string }}-{% if member_role_id %}-{{ member_role_id }}{% endif %}'"
inline-template
key="'{{ sub_form.disabled.name | string }}-{% if member_role_id %}-{{ member_role_id }}{% endif %}'"
v-bind:initial-checked='{{ sub_form.disabled.data|string|lower }}'
v-bind:optional="true"
>
<fieldset data-ally-disabled="true" v-on:change="onInput" class="usa-input__choices revoke-button">
{% set id = "{}-{}".format(sub_form.disabled.name, member_role_id) %}
<div class="form-row" v-if="!isChecked">
<div class="form-col form-col--two-thirds">
<div class="usa-input__title-inline">
{{ sub_form.environment_name.data }}
</div>
<div class="usa-input__help">
{{ role }}
</div>
</div>
<div class="form-col form-col--third">
{{ sub_form.disabled(id=id, checked=True, **{"v-model": "isChecked"}) }}
{{ sub_form.disabled.label(for=id, class="usa-button button-danger-outline") | safe }}
</div>
</div>
<div v-else>
{% call Alert(level='warning') %}
<div class="form-row">
<div class="form-col form-col--two-thirds">
<div class="usa-input__title-inline">
{{ sub_form.environment_name.data }}
</div>
<p class="usa-input__help">
{{ "portfolios.applications.members.form.env_access.revoke_warning" | translate | safe }}
</p>
</div>
<div class="form-col form-col--third">
{{ sub_form.disabled(id=id, checked=True, **{"v-model": "isChecked"}) }}
<label for="{{ id }}" class="link">Undo</label>
</div>
</div>
{% endcall %}
</div>
</fieldset>
</checkboxinput>
{% else %}
<div class="form-row">
<div class="form-col form-col--two-thirds">
<div class="usa-input {% if sub_form.disabled.data or sub_form.role.data == 'No Access' %}env-role__no-access{% endif %}">
<div class='usa-input__title-inline'>
{{ sub_form.environment_name.data }}
</div>
<p class="usa-input__help">
{{ role }}
</p>
</div>
</div>
<div class="form-col form-col--third">
{% if sub_form.role.data == "No Access" and not sub_form.disabled.data -%}
<optionsinput inline-template
v-bind:initial-value="'{{ sub_form.role.data | string }}'"
v-bind:name="'{{ sub_form.name | string }}{% if member_role_id %}-{{ member_role_id }}{% endif %}'"
v-bind:optional="true"
v-bind:watch="true">
<fieldset data-ally-disabled="true" v-on:change="onInput" class="usa-input__choices">
{{ sub_form.role(**{"v-model": "value", "id": "{}-{}".format(sub_form.role.name, member_role_id)}) }}
</fieldset>
</optionsinput>
{% elif sub_form.disabled.data -%}
<p class="usa-input__help">
{{ "portfolios.applications.members.form.env_access.suspended" | translate }}
</p>
{%- endif %}
{{ sub_form.environment_id() }}
</div>
</div>
{% endif %}
{% endmacro %}
{% macro PermsFields(form, new=False, member_role_id=None) %} {% macro PermsFields(form, new=False, member_role_id=None) %}
<h2>{{ "portfolios.applications.members.form.app_perms.title" | translate }}</h2> <h2>{{ "portfolios.applications.members.form.app_perms.title" | translate }}</h2>
<p class='usa-input__help subtitle'>{{ "portfolios.applications.members.form.app_perms.description" | translate | safe}}</p> <p class='usa-input__help subtitle'>{{ "portfolios.applications.members.form.app_perms.description" | translate | safe}}</p>
@ -23,32 +104,15 @@
<hr> <hr>
<div class="environment_roles environment-roles-new"> <div class="environment_roles environment-roles-new">
<h2>{{ "portfolios.applications.members.form.env_access.title" | translate }}</h2> <h2>{{ "portfolios.applications.members.form.env_access.title" | translate }}</h2>
<p class='usa-input__help subtitle'>{{ "portfolios.applications.members.form.env_access.description" | translate | safe }}</p> <p class='usa-input__help subtitle'>
{% if not new -%}
{{ "portfolios.applications.members.form.env_access.edit_description" | translate }}
{%- endif %}
{{ "portfolios.applications.members.form.env_access.description" | translate | safe }}
</p>
<hr> <hr>
{% for environment_data in form.environment_roles %} {% for environment_data in form.environment_roles %}
<optionsinput inline-template {{ EnvRoleInput(environment_data, member_role_id) }}
v-bind:initial-value="'{{ environment_data.role.data | string }}'"
v-bind:name="'{{ environment_data.name | string }}'"
v-bind:optional="true"
v-bind:watch="true">
<div class="usa-input">
<fieldset data-ally-disabled="true" v-on:change="onInput" class="usa-input__choices">
<div class="form-row">
<div class="form-col form-col--two-thirds">
<legend>
<div v-bind:class='["usa-input__title-inline", {"environment-name--gray": value === "None" }]'>
{{ environment_data.environment_name.data }}
</div>
</legend>
</div>
<div class="form-col form-col--third">
{{ environment_data.role(**{"v-model": "value"}) }}
</div>
</div>
</fieldset>
</div>
</optionsinput>
{{ environment_data.environment_id() }}
<hr> <hr>
{% endfor %} {% endfor %}
</div> </div>

View File

@ -36,7 +36,7 @@
{% set modal_name = "edit_member-{}".format(loop.index) %} {% set modal_name = "edit_member-{}".format(loop.index) %}
{% call Modal(modal_name, classes="form-content--app-mem") %} {% call Modal(modal_name, classes="form-content--app-mem") %}
<div class="modal__form--header"> <div class="modal__form--header">
<h1>{{ Icon('avatar') }} {{ member.user_name }}</h1> <h1>{{ Icon('avatar') }} {{ "portfolios.applications.members.form.edit_access_header" | translate({ "user": member.user_name }) }}</h1>
<hr> <hr>
</div> </div>
<base-form inline-template> <base-form inline-template>

View File

@ -45,6 +45,6 @@
{% endset %} {% endset %}
{% call MemberFormTemplate(next_button=next_button) %} {% call MemberFormTemplate(next_button=next_button) %}
{{ member_fields.PermsFields(form=member_form) }} {{ member_fields.PermsFields(form=member_form, new=True) }}
{% endcall %} {% endcall %}
{% endmacro %} {% endmacro %}

View File

@ -1,6 +1,7 @@
import pytest import pytest
from atst.domain.environment_roles import EnvironmentRoles from atst.domain.environment_roles import EnvironmentRoles
from atst.models.environment_role import EnvironmentRole
from tests.factories import * from tests.factories import *
@ -49,7 +50,7 @@ def test_get_by_user_and_environment(application_role, environment):
def test_delete(application_role, environment, monkeypatch): def test_delete(application_role, environment, monkeypatch):
EnvironmentRoleFactory.create( env_role = EnvironmentRoleFactory.create(
application_role=application_role, environment=environment application_role=application_role, environment=environment
) )
assert EnvironmentRoles.delete(application_role.id, environment.id) assert EnvironmentRoles.delete(application_role.id, environment.id)
@ -88,3 +89,14 @@ def test_disable_completed(application_role, environment):
environment_role = EnvironmentRoles.disable(environment_role.id) environment_role = EnvironmentRoles.disable(environment_role.id)
assert environment_role.status == EnvironmentRole.Status.DISABLED assert environment_role.status == EnvironmentRole.Status.DISABLED
def test_get_for_update(application_role, environment):
EnvironmentRoleFactory.create(
application_role=application_role, environment=environment, deleted=True
)
role = EnvironmentRoles.get_for_update(application_role.id, environment.id)
assert role
assert role.application_role == application_role
assert role.environment == environment
assert role.deleted

View File

@ -4,7 +4,7 @@ from uuid import uuid4
from atst.domain.environments import Environments from atst.domain.environments import Environments
from atst.domain.environment_roles import EnvironmentRoles from atst.domain.environment_roles import EnvironmentRoles
from atst.domain.exceptions import NotFoundError from atst.domain.exceptions import NotFoundError, DisabledError
from atst.models.environment_role import CSPRole, EnvironmentRole from atst.models.environment_role import CSPRole, EnvironmentRole
from tests.factories import ( from tests.factories import (
@ -28,8 +28,7 @@ def test_create_environments():
def test_update_env_role(): def test_update_env_role():
env_role = EnvironmentRoleFactory.create(role=CSPRole.BASIC_ACCESS.value) env_role = EnvironmentRoleFactory.create(role=CSPRole.BASIC_ACCESS.value)
new_role = CSPRole.TECHNICAL_READ.value new_role = CSPRole.TECHNICAL_READ.value
Environments.update_env_role(
assert Environments.update_env_role(
env_role.environment, env_role.application_role, new_role env_role.environment, env_role.application_role, new_role
) )
assert env_role.role == new_role assert env_role.role == new_role
@ -37,23 +36,27 @@ def test_update_env_role():
def test_update_env_role_no_access(): def test_update_env_role_no_access():
env_role = EnvironmentRoleFactory.create(role=CSPRole.BASIC_ACCESS.value) env_role = EnvironmentRoleFactory.create(role=CSPRole.BASIC_ACCESS.value)
Environments.update_env_role(env_role.environment, env_role.application_role, None)
assert Environments.update_env_role(
env_role.environment, env_role.application_role, None
)
assert not EnvironmentRoles.get( assert not EnvironmentRoles.get(
env_role.application_role.id, env_role.environment.id env_role.application_role.id, env_role.environment.id
) )
assert env_role.role is None
assert env_role.status == EnvironmentRole.Status.DISABLED
def test_update_env_role_no_change(): def test_update_env_role_disabled_role():
env_role = EnvironmentRoleFactory.create(role=CSPRole.BASIC_ACCESS.value) env_role = EnvironmentRoleFactory.create(role=CSPRole.BASIC_ACCESS.value)
new_role = CSPRole.BASIC_ACCESS.value Environments.update_env_role(env_role.environment, env_role.application_role, None)
assert not Environments.update_env_role( with pytest.raises(DisabledError):
env_role.environment, env_role.application_role, new_role Environments.update_env_role(
) env_role.environment,
env_role.application_role,
CSPRole.TECHNICAL_READ.value,
)
assert env_role.role is None
assert env_role.status == EnvironmentRole.Status.DISABLED
def test_get_excludes_deleted(): def test_get_excludes_deleted():

View File

@ -10,6 +10,7 @@ def test_environment_form():
"environment_id": 123, "environment_id": 123,
"environment_name": "testing", "environment_name": "testing",
"role": ENV_ROLES[0][0], "role": ENV_ROLES[0][0],
"disabled": True,
} }
form = EnvironmentForm(data=form_data) form = EnvironmentForm(data=form_data)
assert form.validate() assert form.validate()
@ -24,6 +25,7 @@ def test_environment_form_default_no_access():
"environment_id": 123, "environment_id": 123,
"environment_name": "testing", "environment_name": "testing",
"role": None, "role": None,
"disabled": False,
} }

View File

@ -129,10 +129,14 @@ def test_edit_application_environments_obj(app, client, user_session):
assert env_obj["name"] == env.name assert env_obj["name"] == env.name
assert env_obj["id"] == env.id assert env_obj["id"] == env.id
assert isinstance(env_obj["edit_form"], EditEnvironmentForm) assert isinstance(env_obj["edit_form"], EditEnvironmentForm)
assert ( assert {
env_obj["members"].sort() "user_name": app_role1.user_name,
== [app_role1.user_name, app_role2.user_name].sort() "status": env_role1.status.value,
) } in env_obj["members"]
assert {
"user_name": app_role2.user_name,
"status": env_role2.status.value,
} in env_obj["members"]
assert isinstance(context["audit_events"], Paginator) assert isinstance(context["audit_events"], Paginator)
@ -503,10 +507,10 @@ def test_update_member(client, user_session, session):
env_1 = EnvironmentFactory.create(application=application) env_1 = EnvironmentFactory.create(application=application)
env_2 = EnvironmentFactory.create(application=application) env_2 = EnvironmentFactory.create(application=application)
# add user to two of the environments: env and env_1 # add user to two of the environments: env and env_1
EnvironmentRoleFactory.create( updated_role = EnvironmentRoleFactory.create(
environment=env, application_role=app_role, role=CSPRole.BASIC_ACCESS.value environment=env, application_role=app_role, role=CSPRole.BASIC_ACCESS.value
) )
EnvironmentRoleFactory.create( suspended_role = EnvironmentRoleFactory.create(
environment=env_1, application_role=app_role, role=CSPRole.BASIC_ACCESS.value environment=env_1, application_role=app_role, role=CSPRole.BASIC_ACCESS.value
) )
@ -524,8 +528,8 @@ def test_update_member(client, user_session, session):
"environment_roles-0-role": CSPRole.TECHNICAL_READ.value, "environment_roles-0-role": CSPRole.TECHNICAL_READ.value,
"environment_roles-0-environment_name": env.name, "environment_roles-0-environment_name": env.name,
"environment_roles-1-environment_id": env_1.id, "environment_roles-1-environment_id": env_1.id,
"environment_roles-1-role": NO_ACCESS,
"environment_roles-1-environment_name": env_1.name, "environment_roles-1-environment_name": env_1.name,
"environment_roles-1-disabled": "True",
"environment_roles-2-environment_id": env_2.id, "environment_roles-2-environment_id": env_2.id,
"environment_roles-2-role": CSPRole.NETWORK_ADMIN.value, "environment_roles-2-role": CSPRole.NETWORK_ADMIN.value,
"environment_roles-2-environment_name": env_2.name, "environment_roles-2-environment_name": env_2.name,
@ -556,11 +560,10 @@ def test_update_member(client, user_session, session):
) )
environment_roles = application.roles[0].environment_roles environment_roles = application.roles[0].environment_roles
# make sure that old env role was deleted and there are only 2 env roles
assert len(environment_roles) == 2
# check that the user has roles in the correct envs # check that the user has roles in the correct envs
assert environment_roles[0].environment in [env, env_2] assert len(environment_roles) == 3
assert environment_roles[1].environment in [env, env_2] assert updated_role.role == CSPRole.TECHNICAL_READ.value
assert suspended_role.status == EnvironmentRole.Status.DISABLED
def test_revoke_invite(client, user_session): def test_revoke_invite(client, user_session):

View File

@ -404,11 +404,17 @@ portfolios:
env_access: env_access:
title: Environment Roles title: Environment Roles
table_header: Environment Access table_header: Environment Access
edit_description: Add or revoke Environment access.
description: Additional role controls are available in the CSP console. <a href="#"> Learn More </a> description: Additional role controls are available in the CSP console. <a href="#"> Learn More </a>
revoke_warning: Save changes to revoke access, <strong>this can not be undone.</strong>
suspended: Suspended access cannot be modified.
next_button: "Next: Permissions" next_button: "Next: Permissions"
app_perms: app_perms:
title: Application Permissions title: Application Permissions
description: Application permissions allow users to provision and modify applications and teams. <a href="#"> Learn More </a> description: Application permissions allow users to provision and modify applications and teams. <a href="#"> Learn More </a>
edit_access_header: "Manage {user}'s Access"
env_access_text: "Add or revoke Environment access. Additional controls are available in the CSP console."
step_2_title: Set Permissions and Roles
add_member: Add Member add_member: Add Member
menu: menu:
edit: Edit Roles and Permissions edit: Edit Roles and Permissions