diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 515c7557..fd66d61d 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -113,15 +113,6 @@ class Environments(object): environment=environment, user=member, new_role=new_role ) - @classmethod - def get_members_by_role(cls, env, role): - return ( - db.session.query(EnvironmentRole) - .filter(EnvironmentRole.environment_id == env.id) - .filter(EnvironmentRole.role == role) - .all() - ) - @classmethod def revoke_access(cls, environment, target_user): EnvironmentRoles.delete(environment.id, target_user.id) diff --git a/atst/models/application_role.py b/atst/models/application_role.py index 7b343cd8..0a5ab672 100644 --- a/atst/models/application_role.py +++ b/atst/models/application_role.py @@ -1,12 +1,14 @@ from enum import Enum from sqlalchemy import Index, ForeignKey, Column, Enum as SQLAEnum, Table from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship +from sqlalchemy.orm import object_session, relationship from sqlalchemy.event import listen from atst.utils import first_or_none from atst.models import Base, mixins from atst.models.mixins.auditable import record_permission_sets_updates +from atst.models.environment import Environment +from atst.models.environment_role import EnvironmentRole from .types import Id @@ -91,6 +93,22 @@ class ApplicationRole( "portfolio": self.application.portfolio.name, } + @property + def environment_roles(self): + if getattr(self, "_environment_roles", None) is None: + roles = ( + object_session(self) + .query(EnvironmentRole) + .join(Environment, Environment.application_id == self.application_id) + .filter(EnvironmentRole.environment_id == Environment.id) + .filter(EnvironmentRole.user_id == self.user_id) + .all() + ) + + setattr(self, "_environment_roles", roles) + + return self._environment_roles + Index( "application_role_user_application", diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index a50e04ae..30bd9f20 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -30,54 +30,54 @@ def get_environments_obj_for_app(application): return environments_obj -def serialize_members(member_list, role): - serialized_list = [] - - for member in member_list: - serialized_list.append( - { - "user_id": str(member.user_id), - "user_name": member.user.full_name, - "role_name": role, - } - ) - - return serialized_list - - -def sort_env_users_by_role(env): - users_list = [] - no_access_users = env.application.users - env.users - no_access_list = [ - {"user_id": str(user.id), "user_name": user.full_name, "role_name": NO_ACCESS} - for user in no_access_users - ] - users_list.append({"role": NO_ACCESS, "members": no_access_list}) - - for role in CSPRole: - users_list.append( - { - "role": role.value, - "members": serialize_members( - Environments.get_members_by_role(env, role.value), role.value - ), - } - ) - - return users_list - - def data_for_app_env_roles_form(application): - data = {"envs": []} - for environment in application.environments: - data["envs"].append( - { - "env_id": environment.id, - "team_roles": sort_env_users_by_role(environment), - } - ) + csp_roles = [role.value for role in CSPRole] + csp_roles.insert(0, NO_ACCESS) + # dictionary for sorting application members by environments + # and roles within those environments + environments_dict = { + e.id: {role_name: [] for role_name in csp_roles} + for e in application.environments + } + for member in application.members: + env_ids = set(environments_dict.keys()) + for env_role in member.environment_roles: + role_members_list = environments_dict[env_role.environment_id][ + env_role.role + ] + role_members_list.append( + { + "user_id": str(member.user.id), + "user_name": member.user_name, + "role_name": env_role.role, + } + ) + env_ids.remove(env_role.environment_id) - return data + # any leftover environment IDs are ones the app member + # does not have access to + for env_id in env_ids: + role_members_list = environments_dict[env_id][NO_ACCESS] + role_members_list.append( + { + "user_id": str(member.user.id), + "user_name": member.user_name, + "role_name": NO_ACCESS, + } + ) + + # transform the data into the shape the form needs + nested_data = [ + { + "env_id": env_id, + "team_roles": [ + {"role": role, "members": members} for role, members in roles.items() + ], + } + for env_id, roles in environments_dict.items() + ] + + return {"envs": nested_data} def check_users_are_in_application(user_ids, application): diff --git a/tests/domain/test_environments.py b/tests/domain/test_environments.py index 577c5b93..65190157 100644 --- a/tests/domain/test_environments.py +++ b/tests/domain/test_environments.py @@ -136,36 +136,6 @@ def test_update_env_roles_by_member(): assert not EnvironmentRoles.get(user.id, testing.id) -def test_get_members_by_role(db): - environment = EnvironmentFactory.create() - env_role_1 = EnvironmentRoleFactory.create( - environment=environment, role=CSPRole.BASIC_ACCESS.value - ) - env_role_2 = EnvironmentRoleFactory.create( - environment=environment, role=CSPRole.TECHNICAL_READ.value - ) - env_role_3 = EnvironmentRoleFactory.create( - environment=environment, role=CSPRole.TECHNICAL_READ.value - ) - rando_env = EnvironmentFactory.create() - rando_env_role = EnvironmentRoleFactory.create( - environment=rando_env, role=CSPRole.BASIC_ACCESS.value - ) - - basic_access_members = Environments.get_members_by_role( - environment, CSPRole.BASIC_ACCESS.value - ) - technical_read_members = Environments.get_members_by_role( - environment, CSPRole.TECHNICAL_READ.value - ) - assert basic_access_members == [env_role_1] - assert rando_env_role not in basic_access_members - assert technical_read_members == [env_role_2, env_role_3] - assert ( - Environments.get_members_by_role(environment, CSPRole.BUSINESS_READ.value) == [] - ) - - def test_get_scoped_environments(db): developer = UserFactory.create() portfolio = PortfolioFactory.create( diff --git a/tests/models/test_application_role.py b/tests/models/test_application_role.py index 5bfc102c..40216758 100644 --- a/tests/models/test_application_role.py +++ b/tests/models/test_application_role.py @@ -1,7 +1,7 @@ from atst.domain.permission_sets import PermissionSets from atst.models.audit_event import AuditEvent -from tests.factories import PortfolioFactory, UserFactory +from tests.factories import * def test_has_application_role_history(session): @@ -38,3 +38,13 @@ def test_has_application_role_history(session): old_state, new_state = changed_event.changed_state["permission_sets"] assert old_state == [PermissionSets.VIEW_APPLICATION] assert new_state == [PermissionSets.EDIT_APPLICATION_TEAM] + + +def test_environment_roles(): + application = ApplicationFactory.create() + environment = EnvironmentFactory.create(application=application) + user = UserFactory.create() + application_role = ApplicationRoleFactory.create(application=application, user=user) + environment_role = EnvironmentRoleFactory.create(environment=environment, user=user) + + assert application_role.environment_roles == [environment_role]