WIP: use team form for application team page
This commit is contained in:
parent
926f89d975
commit
0da0f6a0ae
@ -1,7 +1,7 @@
|
|||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
|
|
||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.models import EnvironmentRole
|
from atst.models import EnvironmentRole, Application, Environment
|
||||||
|
|
||||||
|
|
||||||
class EnvironmentRoles(object):
|
class EnvironmentRoles(object):
|
||||||
@ -35,3 +35,15 @@ class EnvironmentRoles(object):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
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()
|
||||||
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms.fields import FormField, FieldList, HiddenField
|
from wtforms.fields import FormField, FieldList, HiddenField, StringField
|
||||||
|
|
||||||
from .application_member import EnvironmentForm
|
from .application_member import EnvironmentForm
|
||||||
from .forms import BaseForm
|
from .forms import BaseForm
|
||||||
@ -43,6 +43,7 @@ class PermissionsForm(FlaskForm):
|
|||||||
|
|
||||||
class MemberForm(FlaskForm):
|
class MemberForm(FlaskForm):
|
||||||
user_id = HiddenField()
|
user_id = HiddenField()
|
||||||
|
user_name = StringField()
|
||||||
environment_roles = FieldList(FormField(EnvironmentForm))
|
environment_roles = FieldList(FormField(EnvironmentForm))
|
||||||
permission_sets = FormField(PermissionsForm)
|
permission_sets = FormField(PermissionsForm)
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class Application(
|
|||||||
back_populates="application",
|
back_populates="application",
|
||||||
primaryjoin="and_(Environment.application_id==Application.id, Environment.deleted==False)",
|
primaryjoin="and_(Environment.application_id==Application.id, Environment.deleted==False)",
|
||||||
)
|
)
|
||||||
|
# TODO: filter condition on this relationship?
|
||||||
roles = relationship("ApplicationRole")
|
roles = relationship("ApplicationRole")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -2,12 +2,14 @@ from flask import render_template, request as http_request, g, url_for, redirect
|
|||||||
|
|
||||||
|
|
||||||
from . import applications_bp
|
from . import applications_bp
|
||||||
from atst.domain.environments import Environments
|
|
||||||
from atst.domain.applications import Applications
|
from atst.domain.applications import Applications
|
||||||
|
from atst.domain.environments import Environments
|
||||||
|
from atst.domain.environment_roles import EnvironmentRoles
|
||||||
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.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
from atst.domain.exceptions import AlreadyExistsError
|
from atst.domain.exceptions import AlreadyExistsError
|
||||||
from atst.forms.application_member import NewForm as NewMemberForm
|
from atst.forms.application_member import NewForm as NewMemberForm
|
||||||
|
from atst.forms.team import TeamForm
|
||||||
from atst.models import Permissions
|
from atst.models import Permissions
|
||||||
from atst.services.invitation import Invitation as InvitationService
|
from atst.services.invitation import Invitation as InvitationService
|
||||||
from atst.utils.flash import formatted_flash as flash
|
from atst.utils.flash import formatted_flash as flash
|
||||||
@ -27,8 +29,10 @@ def team(application_id):
|
|||||||
application = Applications.get(resource_id=application_id)
|
application = Applications.get(resource_id=application_id)
|
||||||
|
|
||||||
environment_users = {}
|
environment_users = {}
|
||||||
|
team_data = []
|
||||||
for member in application.members:
|
for member in application.members:
|
||||||
user_id = member.user.id
|
user_id = member.user.id
|
||||||
|
user_name = member.user.full_name
|
||||||
environment_users[user_id] = {
|
environment_users[user_id] = {
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"delete_access": permission_str(
|
"delete_access": permission_str(
|
||||||
@ -45,18 +49,51 @@ def team(application_id):
|
|||||||
user=member.user, application=application
|
user=member.user, application=application
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
permission_sets = {
|
||||||
|
"perms_env_mgmt": PermissionSets.EDIT_APPLICATION_ENVIRONMENTS
|
||||||
|
if member.has_permission_set(PermissionSets.EDIT_APPLICATION_ENVIRONMENTS)
|
||||||
|
else "",
|
||||||
|
"perms_team_mgmt": PermissionSets.EDIT_APPLICATION_TEAM
|
||||||
|
if member.has_permission_set(PermissionSets.EDIT_APPLICATION_TEAM)
|
||||||
|
else "",
|
||||||
|
"perms_del_env": PermissionSets.DELETE_APPLICATION_ENVIRONMENTS
|
||||||
|
if member.has_permission_set(PermissionSets.DELETE_APPLICATION_ENVIRONMENTS)
|
||||||
|
else "",
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
team_form = TeamForm(data={"members": team_data})
|
||||||
|
|
||||||
env_roles = [
|
env_roles = [
|
||||||
{"environment_id": e.id, "environment_name": e.name}
|
{"environment_id": e.id, "environment_name": e.name}
|
||||||
for e in application.environments
|
for e in application.environments
|
||||||
]
|
]
|
||||||
member_form = NewMemberForm(data={"environment_roles": env_roles})
|
new_member_form = NewMemberForm(data={"environment_roles": env_roles})
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"portfolios/applications/team.html",
|
"portfolios/applications/team.html",
|
||||||
application=application,
|
application=application,
|
||||||
environment_users=environment_users,
|
environment_users=environment_users,
|
||||||
member_form=member_form,
|
team_form=team_form,
|
||||||
|
new_member_form=new_member_form,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,23 +9,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='form-row'>
|
<div class='form-row'>
|
||||||
<div class='form-col form-col--half'>
|
<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>
|
||||||
<div class='form-col form-col--half'>
|
<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>
|
</div>
|
||||||
<div class='form-row'>
|
<div class='form-row'>
|
||||||
<div class='form-col form-col--half'>
|
<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>
|
||||||
<div class='form-col form-col--half'>
|
<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>
|
</div>
|
||||||
<div class='form-row'>
|
<div class='form-row'>
|
||||||
<div class='form-col form-col--half'>
|
<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>
|
||||||
<div class='form-col form-col--half'>
|
<div class='form-col form-col--half'>
|
||||||
</div>
|
</div>
|
||||||
@ -61,7 +61,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% for environment_data in member_form.environment_roles %}
|
{% for environment_data in new_member_form.environment_roles %}
|
||||||
<optionsinput inline-template
|
<optionsinput inline-template
|
||||||
v-bind:initial-value="'{{ environment_data.role.data | string }}'"
|
v-bind:initial-value="'{{ environment_data.role.data | string }}'"
|
||||||
>
|
>
|
||||||
@ -86,9 +86,9 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ "portfolios.applications.members.new.manage_perms" | translate({"application_name": application.name}) }}</h1>
|
<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") }}
|
{{ CheckboxInput(new_member_form.permission_sets.perms_team_mgmt, classes="input__inline-fields") }}
|
||||||
{% call CheckboxInput(member_form.permission_sets.perms_env_mgmt, classes="input__inline-fields") %}
|
{% call CheckboxInput(new_member_form.permission_sets.perms_env_mgmt, classes="input__inline-fields") %}
|
||||||
{% set field=member_form.permission_sets.perms_del_env %}
|
{% set field=new_member_form.permission_sets.perms_del_env %}
|
||||||
<nestedcheckboxinput
|
<nestedcheckboxinput
|
||||||
name='{{ field.name }}'
|
name='{{ field.name }}'
|
||||||
inline-template
|
inline-template
|
||||||
@ -128,7 +128,7 @@
|
|||||||
{% endset %}
|
{% endset %}
|
||||||
{{ MultiStepModalForm(
|
{{ MultiStepModalForm(
|
||||||
'add-app-mem',
|
'add-app-mem',
|
||||||
member_form,
|
new_member_form,
|
||||||
url_for("applications.create_member", application_id=application.id),
|
url_for("applications.create_member", application_id=application.id),
|
||||||
[step_one, step_two],
|
[step_one, step_two],
|
||||||
button_text=("portfolios.admin.add_new_member" | translate),
|
button_text=("portfolios.admin.add_new_member" | translate),
|
||||||
|
46
templates/fragments/applications/read_only_team.html
Normal file
46
templates/fragments/applications/read_only_team.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{% 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 %}
|
@ -61,52 +61,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="accordion-table__items">
|
<ul class="accordion-table__items">
|
||||||
{% for member in application.members %}
|
{% if user_can(permissions.EDIT_APPLICATION_MEMBER) %}
|
||||||
{% set user = member.user %}
|
{# edit version goes here #}
|
||||||
{% set user_info = environment_users[user.id] %}
|
{% include "fragments/applications/read_only_team.html" %}
|
||||||
{% set user_permissions = user_info["permissions"] %}
|
{% elif user_can(permissions.VIEW_APPLICATION_MEMBER) %}
|
||||||
|
{% include "fragments/applications/read_only_team.html" %}
|
||||||
{% macro PermissionField(value) %}
|
{% endif %}
|
||||||
<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 %}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
16
tests/domain/test_environment_roles.py
Normal file
16
tests/domain/test_environment_roles.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user