Merge pull request #801 from dod-ccpo/app-team-permissions

Application Team Table Permissions
This commit is contained in:
montana-mil 2019-05-07 16:27:23 -04:00 committed by GitHub
commit fb7efc6057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 550 additions and 149 deletions

View File

@ -1,6 +1,9 @@
from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models import ApplicationRole, ApplicationRoleStatus
from atst.domain.permission_sets import PermissionSets
from .permission_sets import PermissionSets
from .exceptions import NotFoundError
class ApplicationRoles(object):
@ -28,3 +31,27 @@ class ApplicationRoles(object):
db.session.add(role)
db.session.commit()
@classmethod
def get(cls, user_id, application_id):
try:
app_role = (
db.session.query(ApplicationRole)
.filter_by(user_id=user_id, application_id=application_id)
.one()
)
except NoResultFound:
raise NotFoundError("application_role")
return app_role
@classmethod
def update_permission_sets(cls, application_role, new_perm_sets_names):
application_role.permission_sets = ApplicationRoles._permission_sets_for_names(
new_perm_sets_names
)
db.session.add(application_role)
db.session.commit()
return application_role

View File

@ -1,7 +1,7 @@
from flask import current_app as app
from atst.database import db
from atst.models import EnvironmentRole
from atst.models import EnvironmentRole, Application, Environment
class EnvironmentRoles(object):
@ -35,3 +35,15 @@ class EnvironmentRoles(object):
return True
else:
return False
@classmethod
def get_for_application_and_user(cls, user_id, application_id):
return (
db.session.query(EnvironmentRole)
.join(Environment)
.join(Application, Environment.application_id == Application.id)
.filter(EnvironmentRole.user_id == user_id)
.filter(Application.id == application_id)
.filter(EnvironmentRole.environment_id == Environment.id)
.all()
)

54
atst/forms/team.py Normal file
View File

@ -0,0 +1,54 @@
from flask_wtf import FlaskForm
from wtforms.fields import FormField, FieldList, HiddenField, StringField
from wtforms.validators import Required
from .application_member import EnvironmentForm
from .forms import BaseForm
from atst.forms.fields import SelectField
from atst.domain.permission_sets import PermissionSets
from atst.utils.localization import translate
class PermissionsForm(FlaskForm):
perms_team_mgmt = SelectField(
translate("portfolios.applications.members.new.manage_team"),
choices=[
(PermissionSets.VIEW_APPLICATION, "View only"),
(PermissionSets.EDIT_APPLICATION_TEAM, "Edit access"),
],
)
perms_env_mgmt = SelectField(
translate("portfolios.applications.members.new.manage_envs"),
choices=[
(PermissionSets.VIEW_APPLICATION, "View only"),
(PermissionSets.EDIT_APPLICATION_ENVIRONMENTS, "Edit access"),
],
)
perms_del_env = SelectField(
choices=[
(PermissionSets.VIEW_APPLICATION, "No"),
(PermissionSets.DELETE_APPLICATION_ENVIRONMENTS, "Yes"),
]
)
@property
def data(self):
_data = super().data
_data.pop("csrf_token", None)
permission_sets = []
for field in _data:
if _data[field] is not None:
permission_sets.append(_data[field])
return permission_sets
class MemberForm(FlaskForm):
user_id = HiddenField(validators=[Required()])
user_name = StringField()
environment_roles = FieldList(FormField(EnvironmentForm))
permission_sets = FormField(PermissionsForm)
class TeamForm(BaseForm):
members = FieldList(FormField(MemberForm))

View File

@ -28,6 +28,7 @@ class Application(
back_populates="application",
primaryjoin="and_(Environment.application_id==Application.id, Environment.deleted==False)",
)
# TODO: filter condition on this relationship?
roles = relationship("ApplicationRole")
@property

View File

@ -2,62 +2,120 @@ from flask import render_template, request as http_request, g, url_for, redirect
from . import applications_bp
from atst.domain.environments import Environments
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.permission_sets import PermissionSets
from atst.domain.environment_roles import EnvironmentRoles
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.services.invitation import Invitation as InvitationService
from atst.utils.flash import formatted_flash as flash
from atst.utils.localization import translate
def permission_str(member, edit_perm_set):
def get_form_permission_value(member, edit_perm_set):
if member.has_permission_set(edit_perm_set):
return translate("portfolios.members.permissions.edit_access")
return edit_perm_set
else:
return translate("portfolios.members.permissions.view_only")
return PermissionSets.VIEW_APPLICATION
def get_team_form(application):
team_data = []
for member in application.members:
user_id = member.user.id
user_name = member.user.full_name
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_and_user(
member.user.id, application.id
)
environment_roles = [
{
"environment_id": str(role.environment.id),
"environment_name": role.environment.name,
"role": role.role,
}
for role in roles
]
team_data.append(
{
"user_id": str(user_id),
"user_name": 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("/applications/<application_id>/team")
@user_can(Permissions.VIEW_APPLICATION, message="view portfolio applications")
def team(application_id):
application = Applications.get(resource_id=application_id)
return render_team_page(application)
environment_users = {}
for member in application.members:
user_id = member.user.id
environment_users[user_id] = {
"permissions": {
"delete_access": permission_str(
member, PermissionSets.DELETE_APPLICATION_ENVIRONMENTS
),
"environment_management": permission_str(
member, PermissionSets.EDIT_APPLICATION_ENVIRONMENTS
),
"team_management": permission_str(
member, PermissionSets.EDIT_APPLICATION_TEAM
),
},
"environments": Environments.for_user(
user=member.user, application=application
),
}
env_roles = [
{"environment_id": e.id, "environment_name": e.name}
for e in application.environments
@applications_bp.route("/application/<application_id>/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 in form.members:
app_role = ApplicationRoles.get(member.data["user_id"], application.id)
new_perms = [
perm
for perm in member.data["permission_sets"]
if perm != PermissionSets.VIEW_APPLICATION
]
member_form = NewMemberForm(data={"environment_roles": env_roles})
ApplicationRoles.update_permission_sets(app_role, new_perms)
flash("updated_application_members_permissions")
return render_template(
"portfolios/applications/team.html",
application=application,
environment_users=environment_users,
member_form=member_form,
return redirect(
url_for(
"applications.team",
application_id=application_id,
fragment="application-members",
_anchor="application-members",
)
)
else:
return (render_team_page(application), 400)
@applications_bp.route("/application/<application_id>/members/new", methods=["POST"])

View File

@ -173,6 +173,13 @@ MESSAGES = {
""",
"category": "success",
},
"updated_application_members_permissions": {
"title_template": translate("flash.success"),
"message_template": """
<p>{{ "flash.updated_application_members_permissions" | translate }}</p>
""",
"category": "success",
},
}

View File

@ -1,26 +1,28 @@
import ally from 'ally.js'
import FormMixin from '../../mixins/form'
import textinput from '../text_input'
import optionsinput from '../options_input'
import DateSelector from '../date_selector'
import MultiStepModalForm from './multi_step_modal_form'
import multicheckboxinput from '../multi_checkbox_input'
import checkboxinput from '../checkbox_input'
import DateSelector from '../date_selector'
import FormMixin from '../../mixins/form'
import levelofwarrant from '../levelofwarrant'
import Modal from '../../mixins/modal'
import multicheckboxinput from '../multi_checkbox_input'
import MultiStepModalForm from './multi_step_modal_form'
import optionsinput from '../options_input'
import textinput from '../text_input'
import toggler from '../toggler'
export default {
name: 'base-form',
components: {
textinput,
optionsinput,
DateSelector,
MultiStepModalForm,
multicheckboxinput,
checkboxinput,
DateSelector,
levelofwarrant,
Modal,
multicheckboxinput,
MultiStepModalForm,
optionsinput,
textinput,
toggler,
},
mixins: [FormMixin],
}

View File

@ -1,8 +1,11 @@
import { emitEvent } from '../lib/emitters'
import FormMixin from '../mixins/form'
export default {
name: 'optionsinput',
mixins: [FormMixin],
props: {
name: String,
initialErrors: {
@ -10,6 +13,10 @@ export default {
default: () => [],
},
initialValue: String,
watch: {
type: Boolean,
default: false,
},
},
data: function() {
@ -27,6 +34,7 @@ export default {
emitEvent('field-change', this, {
value: e.target.value,
name: this.name,
watch: this.watch,
})
this.showError = false
this.showValid = true

View File

@ -1,4 +1,5 @@
import FormMixin from '../mixins/form'
import optionsinput from './options_input'
import textinput from './text_input'
export default {
@ -8,6 +9,7 @@ export default {
components: {
textinput,
optionsinput,
},
data: function() {

View File

@ -16,8 +16,7 @@ export default {
const { name, valid, parent_uid } = event
if (typeof this[name] !== undefined) {
this.fields[name] = valid
if (parent_uid === this._uid) {
if (event['parent_uid'] === this._uid || event['watch']) {
this.changed = true
}
}

View File

@ -39,6 +39,15 @@
.accordion-table__item-content {
padding: ($gap * 2);
.usa-input {
margin: 0;
}
select {
border: none;
font-weight: $font-normal;
}
}
.accordion-table__items {

View File

@ -126,6 +126,7 @@ table {
@include h4;
font-size: $lead-font-size;
justify-content: space-between;
flex: 2;
}
}

View File

@ -1,13 +1,14 @@
{% from "components/icon.html" import Icon %}
{% from "components/tooltip.html" import Tooltip %}
{% macro OptionsInput(field, tooltip, inline=False, label=True, disabled=False) -%}
{% macro OptionsInput(field, tooltip, inline=False, label=True, disabled=False, watch=False) -%}
<optionsinput
name='{{ field.name }}'
inline-template
{% if field.errors %}v-bind:initial-errors='{{ field.errors | list }}'{% endif %}
{% if field.data and field.data != "None" %}v-bind:initial-value="'{{ field.data }}'"{% endif %}
key='{{ field.name }}'
v-bind:watch='{{ watch | string | lower }}'
>
<div
v-bind:class="['usa-input', { 'usa-input--error': showError, 'usa-input--success': showValid }]">

View File

@ -9,23 +9,23 @@
</div>
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(member_form.user_data.first_name, validation='requiredField') }}
{{ TextInput(new_member_form.user_data.first_name, validation='requiredField') }}
</div>
<div class='form-col form-col--half'>
{{ TextInput(member_form.user_data.last_name, validation='requiredField') }}
{{ TextInput(new_member_form.user_data.last_name, validation='requiredField') }}
</div>
</div>
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(member_form.user_data.email, validation='email') }}
{{ TextInput(new_member_form.user_data.email, validation='email') }}
</div>
<div class='form-col form-col--half'>
{{ TextInput(member_form.user_data.phone_number, validation='usPhone', optional=True) }}
{{ TextInput(new_member_form.user_data.phone_number, validation='usPhone', optional=True) }}
</div>
</div>
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(member_form.user_data.dod_id, validation='dodId') }}
{{ TextInput(new_member_form.user_data.dod_id, validation='dodId') }}
</div>
<div class='form-col form-col--half'>
</div>
@ -61,7 +61,7 @@
</span>
</div>
</div>
{% for environment_data in member_form.environment_roles %}
{% for environment_data in new_member_form.environment_roles %}
<optionsinput inline-template
v-bind:initial-value="'{{ environment_data.role.data | string }}'"
>
@ -86,9 +86,9 @@
{% endfor %}
</div>
<h1>{{ "portfolios.applications.members.new.manage_perms" | translate({"application_name": application.name}) }}</h1>
{{ CheckboxInput(member_form.permission_sets.perms_team_mgmt, classes="input__inline-fields") }}
{% call CheckboxInput(member_form.permission_sets.perms_env_mgmt, classes="input__inline-fields") %}
{% set field=member_form.permission_sets.perms_del_env %}
{{ CheckboxInput(new_member_form.permission_sets.perms_team_mgmt, classes="input__inline-fields") }}
{% call CheckboxInput(new_member_form.permission_sets.perms_env_mgmt, classes="input__inline-fields") %}
{% set field=new_member_form.permission_sets.perms_del_env %}
<nestedcheckboxinput
name='{{ field.name }}'
inline-template
@ -128,7 +128,7 @@
{% endset %}
{{ MultiStepModalForm(
'add-app-mem',
member_form,
new_member_form,
url_for("applications.create_member", application_id=application.id),
[step_one, step_two],
button_text=("portfolios.admin.add_new_member" | translate),

View File

@ -0,0 +1,48 @@
{% from "components/options_input.html" import OptionsInput %}
<form method='POST' id="team" action='{{ url_for("applications.update_team", application_id=application.id) }}' autocomplete="off" enctype="multipart/form-data">
{{ team_form.csrf_token }}
{% for member_form in team_form.members %}
{% set environment_roles_form = member_form.environment_roles %}
{% set permissions_form = member_form.permission_sets %}
<toggler inline-template>
<li class="accordion-table__item">
<div class="accordion-table__item-content row">
<div class="col col--grow">{{ member_form.user_name.data }}</div>
<div class="col col--grow">{{ OptionsInput(permissions_form.perms_team_mgmt, label=False, watch=True) }}</div>
<div class="col col--grow">{{ OptionsInput(permissions_form.perms_env_mgmt, label=False, watch=True) }}</div>
<div class="col col--grow">{{ OptionsInput(permissions_form.perms_del_env, label=False, watch=True) }}</div>
<div class="col col--grow icon-link icon-link--large accordion-table__item__toggler">
{% set open_html %}
{{ "portfolios.applications.team_settings.environments" | translate }} ({{ environment_roles_form | length }}) {{ Icon('caret_down') }}
{% endset %}
{% set close_html %}
{{ "portfolios.applications.team_settings.environments" | translate }} ({{ environment_roles_form | length }}) {{ Icon('caret_up') }}
{% endset %}
{{
ToggleButton(
open_html=open_html,
close_html=close_html,
section_name="environments"
)
}}
</div>
</div>
{% call ToggleSection(section_name="environments") %}
<ul>
{% for environment_form in environment_roles_form %}
<li class="accordion-table__item__expanded">
{{ environment_form.environment_name.data }}
</li>
{% endfor %}
</ul>
{% endcall %}
{{ member_form.user_id() }}
</li>
</toggler>
{% endfor %}
</form>

View File

@ -0,0 +1,45 @@
{% for member in team_form.members %}
{% set user_permissions = [member.permission_sets.perms_team_mgmt, member.permission_sets.perms_env_mgmt, member.permission_sets.perms_del_env] %}
{% macro PermissionField(value) %}
<div class="col col--grow user-permission{% if "Edit" in value or "Yes" in value %} green{% endif %}">{{ value }}</div>
{% endmacro %}
<toggler inline-template>
<li class="accordion-table__item">
<div class="accordion-table__item-content row">
<div class="col col--grow">{{ member.user_name.data }}</div>
{% for permission in user_permissions %}
{% set perm = dict(permission.choices).get(permission.data) %}
{{ PermissionField(perm) }}
{% endfor %}
<div class="col col--grow icon-link icon-link--large accordion-table__item__toggler">
{% set open_html %}
{{ "portfolios.applications.team_settings.environments" | translate }} ({{ member.environment_roles | length }}) {{ Icon('caret_down') }}
{% endset %}
{% set close_html %}
{{ "portfolios.applications.team_settings.environments" | translate }} ({{ member.environment_roles | length }}) {{ Icon('caret_up') }}
{% endset %}
{{
ToggleButton(
open_html=open_html,
close_html=close_html,
section_name="environments"
)
}}
</div>
</div>
{% call ToggleSection(section_name="environments") %}
<ul>
{% for environment in member.environment_roles %}
<li class="accordion-table__item__expanded">
{{ environment.environment_name.data }}
</li>
{% endfor %}
</ul>
{% endcall %}
</li>
</toggler>
{% endfor %}

View File

@ -2,6 +2,7 @@
{% from "components/empty_state.html" import EmptyState %}
{% from "components/icon.html" import Icon %}
{% from 'components/save_button.html' import SaveButton %}
{% from "components/toggle_list.html" import ToggleButton, ToggleSection %}
{% set secondary_breadcrumb = 'portfolios.applications.team_settings.title' | translate({ "application_name": application.name }) %}
@ -24,18 +25,18 @@
</div>
<section class="member-list application-list" id="application-members">
<base-form inline-template>
<div class='responsive-table-wrapper panel'>
{% if g.matchesPath("application-members") %}
{% include "fragments/flash.html" %}
{% endif %}
<header>
<div class="responsive-table-wrapper__header">
<div class="responsive-table-wrapper__title">
<div class="responsive-table-wrapper__title row">
<div class="h3">
{{ "portfolios.applications.team_settings.section.title" | translate({ "application_name": application.name }) }}
</div>
</div>
<a class='icon-link'>
<a class="icon-link">
{{ Icon('info') }}
{{ "portfolios.admin.settings_info" | translate }}
</a>
@ -48,77 +49,41 @@
{{ "common.name" | translate }}
</div>
<div class="col col--grow">
{{ "portfolios.applications.team_settings.section.table.delete_access" | translate }}
{{ "portfolios.applications.team_settings.section.table.team_management" | translate }}
</div>
<div class="col col--grow">
{{ "portfolios.applications.team_settings.section.table.environment_management" | translate }}
</div>
<div class="col col--grow">
{{ "portfolios.applications.team_settings.section.table.team_management" | translate }}
{{ "portfolios.applications.team_settings.section.table.delete_access" | translate }}
</div>
<div class="col col--grow">
&nbsp;
</div>
</div>
<ul class="accordion-table__items">
{% for member in application.members %}
{% set user = member.user %}
{% set user_info = environment_users[user.id] %}
{% set user_permissions = user_info["permissions"] %}
{% macro PermissionField(value) %}
<div class="col col--grow user-permission{% if "Edit" in value %} green{% endif %}">{{ value }}</div>
{% endmacro %}
<toggler inline-template>
<li class="accordion-table__item">
<div class="accordion-table__item-content row">
<div class="col col--grow">{{ user.full_name }}</div>
{{ PermissionField(user_permissions["delete_access"]) }}
{{ PermissionField(user_permissions["environment_management"]) }}
{{ PermissionField(user_permissions["team_management"]) }}
<div class="col col--grow icon-link icon-link--large accordion-table__item__toggler">
{% set open_html %}
{{ "portfolios.applications.team_settings.environments" | translate }} ({{ user_info['environments'] | length }}) {{ Icon('caret_down') }}
{% endset %}
{% set close_html %}
{{ "portfolios.applications.team_settings.environments" | translate }} ({{ user_info['environments'] | length }}) {{ Icon('caret_up') }}
{% endset %}
{{
ToggleButton(
open_html=open_html,
close_html=close_html,
section_name="environments"
)
}}
</div>
</div>
{% call ToggleSection(section_name="environments") %}
<ul>
{% for environment in user_info["environments"] %}
<li class="accordion-table__item__expanded">
{{ environment.name }}
</li>
{% endfor %}
</ul>
{% endcall %}
</li>
</toggler>
{% endfor %}
{% 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 %}
</ul>
</div>
<div class="members-table-footer">
<div class="panel__footer">
<div class="action-group save">
{% if user_can(permissions.EDIT_APPLICATION_MEMBER) %}
{{ SaveButton(text=('common.save' | translate), element="input", form="team") }}
{% endif %}
{% if user_can(permissions.CREATE_APPLICATION_MEMBER) %}
{% include "fragments/applications/add_new_application_member.html" %}
{% endif %}
</div>
</div>
</form>
</div>
</base-form>
</section>
{% endif %}
{% endblock %}

View File

@ -1,4 +1,7 @@
import pytest
from atst.domain.application_roles import ApplicationRoles
from atst.domain.exceptions import NotFoundError
from atst.domain.permission_sets import PermissionSets
from atst.models import ApplicationRoleStatus
@ -33,3 +36,38 @@ def test_enabled_application_role():
ApplicationRoles.enable(app_role)
assert app_role.status == ApplicationRoleStatus.ACTIVE
def test_get():
user = UserFactory.create()
application = ApplicationFactory.create()
app_role = ApplicationRoleFactory.create(user=user, application=application)
assert ApplicationRoles.get(user.id, application.id)
assert app_role.application == application
assert app_role.user == user
def test_get_handles_invalid_id():
user = UserFactory.create()
application = ApplicationFactory.create()
with pytest.raises(NotFoundError):
ApplicationRoles.get(user.id, application.id)
def test_update_permission_sets():
user = UserFactory.create()
application = ApplicationFactory.create()
app_role = ApplicationRoleFactory.create(user=user, application=application)
view_app = [PermissionSets.get(PermissionSets.VIEW_APPLICATION)]
new_perms_names = [
PermissionSets.EDIT_APPLICATION_TEAM,
PermissionSets.DELETE_APPLICATION_ENVIRONMENTS,
]
new_perms = PermissionSets.get_many(new_perms_names)
# view application permission is included by default
assert app_role.permission_sets == view_app
assert ApplicationRoles.update_permission_sets(app_role, new_perms_names)
assert set(app_role.permission_sets) == set(new_perms + view_app)

View File

@ -0,0 +1,16 @@
from atst.domain.environment_roles import EnvironmentRoles
from tests.factories import *
def test_get_for_application_and_user():
user = UserFactory.create()
application = ApplicationFactory.create()
env1 = EnvironmentFactory.create(application=application)
EnvironmentFactory.create(application=application)
EnvironmentRoleFactory.create(user=user, environment=env1)
roles = EnvironmentRoles.get_for_application_and_user(user.id, application.id)
assert len(roles) == 1
assert roles[0].environment == env1
assert roles[0].user == user

0
tests/forms/__init__.py Normal file
View File

31
tests/forms/test_team.py Normal file
View File

@ -0,0 +1,31 @@
from wtforms.validators import ValidationError
import pytest
from atst.domain.permission_sets import PermissionSets
from atst.forms.team import *
def test_permissions_form_permission_sets():
form_data = {
"perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM,
"perms_env_mgmt": PermissionSets.VIEW_APPLICATION,
"perms_del_env": PermissionSets.VIEW_APPLICATION,
}
form = PermissionsForm(data=form_data)
assert form.validate()
assert form.data == [
PermissionSets.EDIT_APPLICATION_TEAM,
PermissionSets.VIEW_APPLICATION,
PermissionSets.VIEW_APPLICATION,
]
def test_permissions_form_invalid():
form_data = {
"perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM,
"perms_env_mgmt": "not a real choice",
"perms_del_env": PermissionSets.VIEW_APPLICATION,
}
form = PermissionsForm(data=form_data)
assert not form.validate()

View File

View File

@ -1,6 +1,9 @@
import pytest
from flask import url_for
from tests.factories import PortfolioFactory, ApplicationFactory, UserFactory
from atst.domain.permission_sets import PermissionSets
from tests.factories import *
def test_application_team(client, user_session):
@ -10,10 +13,83 @@ def test_application_team(client, user_session):
user_session(portfolio.owner)
response = client.get(url_for("applications.team", application_id=application.id))
assert response.status_code == 200
def test_update_team(client, user_session):
application = ApplicationFactory.create()
owner = application.portfolio.owner
app_role = ApplicationRoleFactory.create(
application=application, permission_sets=[]
)
app_user = app_role.user
user_session(owner)
response = client.post(
url_for("applications.update_team", application_id=application.id),
data={
"members-0-user_id": app_user.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=[]
)
app_user = app_role.user
permission_sets = app_user.permission_sets
user_session(owner)
response = client.post(
url_for("applications.update_team", application_id=application.id),
data={
"members-0-user_id": app_user.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_user.permission_sets == permission_sets
def test_update_team_with_non_app_user(client, user_session):
application = ApplicationFactory.create()
owner = application.portfolio.owner
app_role = ApplicationRoleFactory.create(
application=application, permission_sets=[]
)
non_app_user = UserFactory.create()
app_user = app_role.user
user_session(owner)
response = client.post(
url_for("applications.update_team", application_id=application.id),
data={
"members-0-user_id": non_app_user.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 == 404
def test_create_member(client, user_session):
user = UserFactory.create()
application = ApplicationFactory.create(

View File

@ -73,6 +73,7 @@ flash:
portfolio_home: Go to my portfolio home page
success: Success!
new_application_member: 'You have successfully invited {user_name} to the team.'
updated_application_members_permissions: 'You have successfully updated member permissions.'
footer:
about_link_text: Joint Enterprise Defense Infrastructure
browser_support: JEDI Cloud supported on these web browsers