Merge pull request #701 from dod-ccpo/portfolio-perms

Portfolio perms
This commit is contained in:
dandds
2019-03-18 08:40:35 -04:00
committed by GitHub
60 changed files with 940 additions and 738 deletions

View File

@@ -27,7 +27,7 @@ class Applications(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.VIEW_APPLICATION_IN_PORTFOLIO,
Permissions.VIEW_APPLICATION,
"view application in portfolio",
)
@@ -56,7 +56,7 @@ class Applications(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.VIEW_APPLICATION_IN_PORTFOLIO,
Permissions.VIEW_APPLICATION,
"view application in portfolio",
)

View File

@@ -36,6 +36,7 @@ class AuditLog(object):
@classmethod
def get_all_events(cls, user, pagination_opts=None):
# TODO: general audit log permissions
Authorization.check_atat_permission(
user, Permissions.VIEW_AUDIT_LOG, "view audit log"
)
@@ -46,7 +47,7 @@ class AuditLog(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.VIEW_PORTFOLIO_AUDIT_LOG,
Permissions.VIEW_PORTFOLIO_ACTIVITY_LOG,
"view portfolio audit log",
)
return AuditEventQuery.get_ws_events(portfolio.id, pagination_opts)

View File

@@ -1,4 +1,4 @@
from atst.domain.portfolio_roles import PortfolioRoles
from atst.utils import first_or_none
from atst.models.permissions import Permissions
from atst.domain.exceptions import UnauthorizedError
@@ -6,9 +6,13 @@ from atst.domain.exceptions import UnauthorizedError
class Authorization(object):
@classmethod
def has_portfolio_permission(cls, user, portfolio, permission):
return permission in PortfolioRoles.portfolio_role_permissions(
portfolio, user
) or Authorization.is_ccpo(user)
port_role = first_or_none(
lambda pr: pr.portfolio == portfolio, user.portfolio_roles
)
if port_role:
return permission in port_role.permissions
else:
return False
@classmethod
def has_atat_permission(cls, user, permission):

View File

@@ -64,7 +64,7 @@ class Environments(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.ADD_AND_ASSIGN_CSP_ROLES,
Permissions.EDIT_APPLICATION_MEMBER,
"assign environment roles",
)
updated = False
@@ -104,7 +104,7 @@ class Environments(object):
Authorization.check_portfolio_permission(
user,
environment.portfolio,
Permissions.REMOVE_CSP_ROLES,
Permissions.EDIT_APPLICATION_MEMBER,
"revoke environment access",
)
EnvironmentRoles.delete(environment.id, target_user.id)

View File

@@ -119,7 +119,7 @@ class Invitations(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.CREATE_PORTFOLIO_USERS,
"resend a portfolio invitation",
)

View File

@@ -0,0 +1,169 @@
from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models import PermissionSet, Permissions
from .exceptions import NotFoundError
class PermissionSets(object):
VIEW_PORTFOLIO = "view_portfolio"
VIEW_PORTFOLIO_APPLICATION_MANAGEMENT = "view_portfolio_application_management"
VIEW_PORTFOLIO_FUNDING = "view_portfolio_funding"
VIEW_PORTFOLIO_REPORTS = "view_portfolio_reports"
VIEW_PORTFOLIO_ADMIN = "view_portfolio_admin"
EDIT_PORTFOLIO_APPLICATION_MANAGEMENT = "edit_portfolio_application_management"
EDIT_PORTFOLIO_FUNDING = "edit_portfolio_funding"
EDIT_PORTFOLIO_REPORTS = "edit_portfolio_reports"
EDIT_PORTFOLIO_ADMIN = "edit_portfolio_admin"
PORTFOLIO_POC = "portfolio_poc"
@classmethod
def get(cls, perms_set_name):
try:
role = db.session.query(PermissionSet).filter_by(name=perms_set_name).one()
except NoResultFound:
raise NotFoundError("permission_set")
return role
@classmethod
def get_all(cls):
return db.session.query(PermissionSet).all()
@classmethod
def get_many(cls, perms_set_names):
return (
db.session.query(PermissionSet)
.filter(PermissionSet.name.in_(perms_set_names))
.all()
)
ATAT_ROLES = [
{
"name": "ccpo",
"display_name": "CCPO",
"description": "",
"permissions": [Permissions.VIEW_AUDIT_LOG],
},
{
"name": "default",
"display_name": "Default",
"description": "",
"permissions": [],
},
]
_PORTFOLIO_BASIC_PERMISSION_SETS = [
{
"name": PermissionSets.VIEW_PORTFOLIO,
"description": "View basic portfolio info",
"display_name": "View Portfolio",
"permissions": [Permissions.VIEW_PORTFOLIO],
}
]
_PORTFOLIO_APP_MGMT_PERMISSION_SETS = [
{
"name": PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT,
"description": "View applications and related resources",
"display_name": "Application Management",
"permissions": [
Permissions.VIEW_APPLICATION,
Permissions.VIEW_APPLICATION_MEMBER,
Permissions.VIEW_ENVIRONMENT,
],
},
{
"name": PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT,
"description": "Edit applications and related resources",
"display_name": "Application Management",
"permissions": [
Permissions.EDIT_APPLICATION,
Permissions.CREATE_APPLICATION,
Permissions.EDIT_APPLICATION_MEMBER,
Permissions.CREATE_APPLICATION_MEMBER,
Permissions.EDIT_ENVIRONMENT,
Permissions.CREATE_ENVIRONMENT,
],
},
]
_PORTFOLIO_FUNDING_PERMISSION_SETS = [
{
"name": PermissionSets.VIEW_PORTFOLIO_FUNDING,
"description": "View a portfolio's task orders",
"display_name": "Funding",
"permissions": [
Permissions.VIEW_PORTFOLIO_FUNDING,
Permissions.VIEW_TASK_ORDER_DETAILS,
],
},
{
"name": PermissionSets.EDIT_PORTFOLIO_FUNDING,
"description": "Edit a portfolio's task orders and add new ones",
"display_name": "Funding",
"permissions": [
Permissions.CREATE_TASK_ORDER,
Permissions.EDIT_TASK_ORDER_DETAILS,
],
},
]
_PORTFOLIO_REPORTS_PERMISSION_SETS = [
{
"name": PermissionSets.VIEW_PORTFOLIO_REPORTS,
"description": "View a portfolio's reports",
"display_name": "Reporting",
"permissions": [Permissions.VIEW_PORTFOLIO_REPORTS],
},
{
"name": PermissionSets.EDIT_PORTFOLIO_REPORTS,
"description": "Edit a portfolio's reports (no-op)",
"display_name": "Reporting",
"permissions": [],
},
]
_PORTFOLIO_ADMIN_PERMISSION_SETS = [
{
"name": PermissionSets.VIEW_PORTFOLIO_ADMIN,
"description": "View a portfolio's admin options",
"display_name": "Portfolio Administration",
"permissions": [
Permissions.VIEW_PORTFOLIO_ADMIN,
Permissions.VIEW_PORTFOLIO_NAME,
Permissions.VIEW_PORTFOLIO_USERS,
Permissions.VIEW_PORTFOLIO_ACTIVITY_LOG,
Permissions.VIEW_PORTFOLIO_POC,
],
},
{
"name": PermissionSets.EDIT_PORTFOLIO_ADMIN,
"description": "Edit a portfolio's admin options",
"display_name": "Portfolio Administration",
"permissions": [
Permissions.EDIT_PORTFOLIO_NAME,
Permissions.EDIT_PORTFOLIO_USERS,
Permissions.CREATE_PORTFOLIO_USERS,
],
},
]
_PORTFOLIO_POC_PERMISSION_SETS = [
{
"name": "portfolio_poc",
"description": "Permissions belonging to the Portfolio POC",
"display_name": "Portfolio Point of Contact",
"permissions": [Permissions.EDIT_PORTFOLIO_POC, Permissions.ARCHIVE_PORTFOLIO],
}
]
PORTFOLIO_PERMISSION_SETS = (
_PORTFOLIO_BASIC_PERMISSION_SETS
+ _PORTFOLIO_APP_MGMT_PERMISSION_SETS
+ _PORTFOLIO_FUNDING_PERMISSION_SETS
+ _PORTFOLIO_REPORTS_PERMISSION_SETS
+ _PORTFOLIO_ADMIN_PERMISSION_SETS
+ _PORTFOLIO_POC_PERMISSION_SETS
)

View File

@@ -8,8 +8,7 @@ from atst.models.portfolio_role import (
)
from atst.models.user import User
from .roles import Roles
from .users import Users
from .permission_sets import PermissionSets
from .exceptions import NotFoundError
@@ -53,17 +52,6 @@ class PortfolioRoles(object):
except NoResultFound:
return None
@classmethod
def portfolio_role_permissions(cls, portfolio, user):
portfolio_role = PortfolioRoles._get_active_portfolio_role(
portfolio.id, user.id
)
atat_permissions = set(user.atat_role.permissions)
portfolio_permissions = (
[] if portfolio_role is None else portfolio_role.role.permissions
)
return set(portfolio_permissions).union(atat_permissions)
@classmethod
def _get_portfolio_role(cls, user, portfolio_id):
try:
@@ -80,9 +68,7 @@ class PortfolioRoles(object):
raise NotFoundError("portfolio role")
@classmethod
def add(cls, user, portfolio_id, role_name):
role = Roles.get(role_name)
def add(cls, user, portfolio_id, permission_sets=None):
new_portfolio_role = None
try:
existing_portfolio_role = (
@@ -94,13 +80,14 @@ class PortfolioRoles(object):
.one()
)
new_portfolio_role = existing_portfolio_role
new_portfolio_role.role = role
except NoResultFound:
new_portfolio_role = PortfolioRole(
user=user,
role_id=role.id,
portfolio_id=portfolio_id,
status=PortfolioRoleStatus.PENDING,
user=user, portfolio_id=portfolio_id, status=PortfolioRoleStatus.PENDING
)
if permission_sets:
new_portfolio_role.permission_sets = PortfolioRoles._permission_sets_for_names(
permission_sets
)
user.portfolio_roles.append(new_portfolio_role)
@@ -109,56 +96,41 @@ class PortfolioRoles(object):
return new_portfolio_role
DEFAULT_PORTFOLIO_PERMISSION_SETS = {
PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT,
PermissionSets.VIEW_PORTFOLIO_FUNDING,
PermissionSets.VIEW_PORTFOLIO_REPORTS,
PermissionSets.VIEW_PORTFOLIO_ADMIN,
PermissionSets.VIEW_PORTFOLIO,
}
PORTFOLIO_PERMISSION_SETS = DEFAULT_PORTFOLIO_PERMISSION_SETS.union(
{
PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT,
PermissionSets.EDIT_PORTFOLIO_FUNDING,
PermissionSets.EDIT_PORTFOLIO_REPORTS,
PermissionSets.EDIT_PORTFOLIO_ADMIN,
PermissionSets.PORTFOLIO_POC,
}
)
@classmethod
def update_role(cls, portfolio_role, role_name):
new_role = Roles.get(role_name)
portfolio_role.role = new_role
def _permission_sets_for_names(cls, set_names):
perms_set_names = PortfolioRoles.DEFAULT_PORTFOLIO_PERMISSION_SETS.union(
set(set_names)
)
return PermissionSets.get_many(perms_set_names)
@classmethod
def update(cls, portfolio_role, set_names):
new_permission_sets = PortfolioRoles._permission_sets_for_names(set_names)
portfolio_role.permission_sets = new_permission_sets
db.session.add(portfolio_role)
db.session.commit()
return portfolio_role
@classmethod
def add_many(cls, portfolio_id, portfolio_role_dicts):
portfolio_roles = []
for user_dict in portfolio_role_dicts:
try:
user = Users.get(user_dict["id"])
except NoResultFound:
default_role = Roles.get("developer")
user = User(id=user_dict["id"], atat_role=default_role)
try:
role = Roles.get(user_dict["portfolio_role"])
except NoResultFound:
raise NotFoundError("role")
try:
existing_portfolio_role = (
db.session.query(PortfolioRole)
.filter(
PortfolioRole.user == user,
PortfolioRole.portfolio_id == portfolio_id,
)
.one()
)
new_portfolio_role = existing_portfolio_role
new_portfolio_role.role = role
except NoResultFound:
new_portfolio_role = PortfolioRole(
user=user, role_id=role.id, portfolio_id=portfolio_id
)
user.portfolio_roles.append(new_portfolio_role)
portfolio_roles.append(new_portfolio_role)
db.session.add(user)
db.session.commit()
return portfolio_roles
@classmethod
def enable(cls, portfolio_role):
portfolio_role.status = PortfolioRoleStatus.ACTIVE

View File

@@ -1,4 +1,4 @@
from atst.domain.roles import Roles
from atst.domain.permission_sets import PermissionSets
from atst.domain.authz import Authorization
from atst.models.permissions import Permissions
from atst.domain.users import Users
@@ -20,8 +20,12 @@ class Portfolios(object):
portfolio = PortfoliosQuery.create(
name=name, defense_component=defense_component
)
perms_sets = PermissionSets.get_many(PortfolioRoles.PORTFOLIO_PERMISSION_SETS)
Portfolios._create_portfolio_role(
user, portfolio, "owner", status=PortfolioRoleStatus.ACTIVE
user,
portfolio,
status=PortfolioRoleStatus.ACTIVE,
permission_sets=perms_sets,
)
PortfoliosQuery.add_and_commit(portfolio)
return portfolio
@@ -39,7 +43,7 @@ class Portfolios(object):
def get_for_update_applications(cls, user, portfolio_id):
portfolio = PortfoliosQuery.get(portfolio_id)
Authorization.check_portfolio_permission(
user, portfolio, Permissions.ADD_APPLICATION_IN_PORTFOLIO, "add application"
user, portfolio, Permissions.CREATE_APPLICATION, "add application"
)
return portfolio
@@ -50,7 +54,7 @@ class Portfolios(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.EDIT_PORTFOLIO_INFORMATION,
Permissions.EDIT_PORTFOLIO_NAME,
"update portfolio information",
)
@@ -62,7 +66,7 @@ class Portfolios(object):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.EDIT_PORTFOLIO_USERS,
"update a portfolio member",
)
@@ -72,10 +76,7 @@ class Portfolios(object):
def get_with_members(cls, user, portfolio_id):
portfolio = PortfoliosQuery.get(portfolio_id)
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.VIEW_PORTFOLIO_MEMBERS,
"view portfolio members",
user, portfolio, Permissions.VIEW_PORTFOLIO_USERS, "view portfolio members"
)
return portfolio
@@ -91,10 +92,7 @@ class Portfolios(object):
@classmethod
def create_member(cls, user, portfolio, data):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
"create portfolio member",
user, portfolio, Permissions.EDIT_PORTFOLIO_USERS, "create portfolio member"
)
new_user = Users.get_or_create_by_dod_id(
@@ -105,31 +103,34 @@ class Portfolios(object):
atat_role_name="default",
provisional=True,
)
return Portfolios.add_member(portfolio, new_user, data["portfolio_role"])
permission_sets = data.get("permission_sets", [])
return Portfolios.add_member(
portfolio, new_user, permission_sets=permission_sets
)
@classmethod
def add_member(cls, portfolio, member, role_name):
portfolio_role = PortfolioRoles.add(member, portfolio.id, role_name)
def add_member(cls, portfolio, member, permission_sets=None):
portfolio_role = PortfolioRoles.add(member, portfolio.id, permission_sets)
return portfolio_role
@classmethod
def update_member(cls, user, portfolio, member, role_name):
def update_member(cls, user, portfolio, member, permission_sets):
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
"edit portfolio member",
user, portfolio, Permissions.EDIT_PORTFOLIO_USERS, "edit portfolio member"
)
return PortfolioRoles.update_role(member, role_name)
# need to update perms sets here
return PortfolioRoles.update(member, permission_sets)
@classmethod
def _create_portfolio_role(
cls, user, portfolio, role_name, status=PortfolioRoleStatus.PENDING
cls, user, portfolio, status=PortfolioRoleStatus.PENDING, permission_sets=None
):
role = Roles.get(role_name)
if permission_sets is None:
permission_sets = []
portfolio_role = PortfoliosQuery.create_portfolio_role(
user, role, portfolio, status=status
user, portfolio, status=status, permission_sets=permission_sets
)
PortfoliosQuery.add_and_commit(portfolio_role)
return portfolio_role
@@ -152,10 +153,7 @@ class Portfolios(object):
def revoke_access(cls, user, portfolio_id, portfolio_role_id):
portfolio = PortfoliosQuery.get(portfolio_id)
Authorization.check_portfolio_permission(
user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
"revoke portfolio access",
user, portfolio, Permissions.EDIT_PORTFOLIO_USERS, "revoke portfolio access"
)
portfolio_role = PortfolioRoles.get_by_id(portfolio_role_id)

View File

@@ -18,5 +18,5 @@ class PortfoliosQuery(Query):
)
@classmethod
def create_portfolio_role(cls, user, role, portfolio, **kwargs):
return PortfolioRole(user=user, role=role, portfolio=portfolio, **kwargs)
def create_portfolio_role(cls, user, portfolio, **kwargs):
return PortfolioRole(user=user, portfolio=portfolio, **kwargs)

View File

@@ -31,7 +31,7 @@ class ScopedPortfolio(ScopedResource):
@property
def applications(self):
can_view_all_applications = Authorization.has_portfolio_permission(
self.user, self.resource, Permissions.VIEW_APPLICATION_IN_PORTFOLIO
self.user, self.resource, Permissions.VIEW_APPLICATION
)
if can_view_all_applications:
@@ -54,9 +54,7 @@ class ScopedApplication(ScopedResource):
@property
def environments(self):
can_view_all_environments = Authorization.has_portfolio_permission(
self.user,
self.resource.portfolio,
Permissions.VIEW_ENVIRONMENT_IN_APPLICATION,
self.user, self.resource.portfolio, Permissions.VIEW_ENVIRONMENT
)
if can_view_all_environments:

View File

@@ -1,177 +0,0 @@
from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models import Role, Permissions
from .exceptions import NotFoundError
ATAT_ROLES = [
{
"name": "ccpo",
"display_name": "CCPO",
"description": "",
"permissions": [
Permissions.VIEW_ORIGINAL_JEDI_REQEUST,
Permissions.REVIEW_AND_APPROVE_JEDI_PORTFOLIO_REQUEST,
Permissions.MODIFY_ATAT_ROLE_PERMISSIONS,
Permissions.CREATE_CSP_ROLE,
Permissions.DELETE_CSP_ROLE,
Permissions.DEACTIVE_CSP_ROLE,
Permissions.MODIFY_CSP_ROLE_PERMISSIONS,
Permissions.VIEW_USAGE_REPORT,
Permissions.VIEW_USAGE_DOLLARS,
Permissions.ADD_AND_ASSIGN_CSP_ROLES,
Permissions.REMOVE_CSP_ROLES,
Permissions.REQUEST_NEW_CSP_ROLE,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS,
Permissions.VIEW_ASSIGNED_CSP_ROLE_CONFIGURATIONS,
Permissions.DEACTIVATE_PORTFOLIO,
Permissions.VIEW_ATAT_PERMISSIONS,
Permissions.TRANSFER_OWNERSHIP_OF_PORTFOLIO,
Permissions.VIEW_PORTFOLIO,
Permissions.VIEW_PORTFOLIO_MEMBERS,
Permissions.ADD_APPLICATION_IN_PORTFOLIO,
Permissions.DELETE_APPLICATION_IN_PORTFOLIO,
Permissions.DEACTIVATE_APPLICATION_IN_PORTFOLIO,
Permissions.VIEW_APPLICATION_IN_PORTFOLIO,
Permissions.RENAME_APPLICATION_IN_PORTFOLIO,
Permissions.ADD_ENVIRONMENT_IN_APPLICATION,
Permissions.DELETE_ENVIRONMENT_IN_APPLICATION,
Permissions.DEACTIVATE_ENVIRONMENT_IN_APPLICATION,
Permissions.VIEW_ENVIRONMENT_IN_APPLICATION,
Permissions.RENAME_ENVIRONMENT_IN_APPLICATION,
Permissions.ADD_TAG_TO_PORTFOLIO,
Permissions.REMOVE_TAG_FROM_PORTFOLIO,
Permissions.VIEW_AUDIT_LOG,
Permissions.VIEW_PORTFOLIO_AUDIT_LOG,
],
},
{
"name": "default",
"display_name": "Default",
"description": "",
"permissions": [Permissions.REQUEST_JEDI_PORTFOLIO],
},
]
PORTFOLIO_ROLES = [
{
"name": "owner",
"display_name": "Portfolio Owner",
"description": "Adds, edits, deactivates access to all applications, environments, and members. Views budget reports. Initiates and edits JEDI Cloud requests.",
"permissions": [
Permissions.REQUEST_JEDI_PORTFOLIO,
Permissions.VIEW_ORIGINAL_JEDI_REQEUST,
Permissions.VIEW_USAGE_REPORT,
Permissions.VIEW_USAGE_DOLLARS,
Permissions.ADD_AND_ASSIGN_CSP_ROLES,
Permissions.REMOVE_CSP_ROLES,
Permissions.REQUEST_NEW_CSP_ROLE,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS,
Permissions.VIEW_ASSIGNED_CSP_ROLE_CONFIGURATIONS,
Permissions.DEACTIVATE_PORTFOLIO,
Permissions.VIEW_ATAT_PERMISSIONS,
Permissions.VIEW_PORTFOLIO,
Permissions.VIEW_PORTFOLIO_MEMBERS,
Permissions.EDIT_PORTFOLIO_INFORMATION,
Permissions.ADD_APPLICATION_IN_PORTFOLIO,
Permissions.DELETE_APPLICATION_IN_PORTFOLIO,
Permissions.DEACTIVATE_APPLICATION_IN_PORTFOLIO,
Permissions.VIEW_APPLICATION_IN_PORTFOLIO,
Permissions.RENAME_APPLICATION_IN_PORTFOLIO,
Permissions.ADD_ENVIRONMENT_IN_APPLICATION,
Permissions.DELETE_ENVIRONMENT_IN_APPLICATION,
Permissions.DEACTIVATE_ENVIRONMENT_IN_APPLICATION,
Permissions.VIEW_ENVIRONMENT_IN_APPLICATION,
Permissions.RENAME_ENVIRONMENT_IN_APPLICATION,
Permissions.VIEW_PORTFOLIO_AUDIT_LOG,
Permissions.VIEW_TASK_ORDER,
Permissions.UPDATE_TASK_ORDER,
Permissions.ADD_TASK_ORDER_OFFICER,
],
},
{
"name": "admin",
"display_name": "Administrator",
"description": "Adds and edits applications, environments, members, but cannot deactivate. Cannot view budget reports or JEDI Cloud requests.",
"permissions": [
Permissions.VIEW_USAGE_REPORT,
Permissions.ADD_AND_ASSIGN_CSP_ROLES,
Permissions.REMOVE_CSP_ROLES,
Permissions.REQUEST_NEW_CSP_ROLE,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS,
Permissions.VIEW_ASSIGNED_CSP_ROLE_CONFIGURATIONS,
Permissions.VIEW_PORTFOLIO,
Permissions.VIEW_PORTFOLIO_MEMBERS,
Permissions.EDIT_PORTFOLIO_INFORMATION,
Permissions.ADD_APPLICATION_IN_PORTFOLIO,
Permissions.DELETE_APPLICATION_IN_PORTFOLIO,
Permissions.DEACTIVATE_APPLICATION_IN_PORTFOLIO,
Permissions.VIEW_APPLICATION_IN_PORTFOLIO,
Permissions.RENAME_APPLICATION_IN_PORTFOLIO,
Permissions.ADD_ENVIRONMENT_IN_APPLICATION,
Permissions.DELETE_ENVIRONMENT_IN_APPLICATION,
Permissions.DEACTIVATE_ENVIRONMENT_IN_APPLICATION,
Permissions.VIEW_ENVIRONMENT_IN_APPLICATION,
Permissions.RENAME_ENVIRONMENT_IN_APPLICATION,
Permissions.VIEW_PORTFOLIO_AUDIT_LOG,
Permissions.VIEW_TASK_ORDER,
Permissions.UPDATE_TASK_ORDER,
Permissions.ADD_TASK_ORDER_OFFICER,
],
},
{
"name": "developer",
"display_name": "Developer",
"description": "Views only the applications and environments they are granted access to. Can also view members associated with each environment.",
"permissions": [Permissions.VIEW_USAGE_REPORT, Permissions.VIEW_PORTFOLIO],
},
{
"name": "billing_auditor",
"display_name": "Billing Auditor",
"description": "Views only the applications and environments they are granted access to. Can also view budgets and reports associated with the portfolio.",
"permissions": [
Permissions.VIEW_USAGE_REPORT,
Permissions.VIEW_USAGE_DOLLARS,
Permissions.VIEW_PORTFOLIO,
],
},
{
"name": "security_auditor",
"description": "Views only the applications and environments they are granted access to. Can also view activity logs.",
"display_name": "Security Auditor",
"permissions": [
Permissions.VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS,
Permissions.VIEW_ASSIGNED_CSP_ROLE_CONFIGURATIONS,
Permissions.VIEW_ATAT_PERMISSIONS,
Permissions.VIEW_PORTFOLIO,
],
},
{
"name": "officer",
"description": "Officer involved with setting up a Task Order",
"display_name": "Task Order Officer",
"permissions": [
Permissions.VIEW_PORTFOLIO,
Permissions.VIEW_USAGE_REPORT,
Permissions.VIEW_USAGE_DOLLARS,
],
},
]
class Roles(object):
@classmethod
def get(cls, role_name):
try:
role = db.session.query(Role).filter_by(name=role_name).one()
except NoResultFound:
raise NotFoundError("role")
return role
@classmethod
def get_all(cls):
return db.session.query(Role).all()

View File

@@ -7,6 +7,7 @@ from atst.models.permissions import Permissions
from atst.models.dd_254 import DD254
from atst.domain.portfolios import Portfolios
from atst.domain.authz import Authorization
from atst.domain.permission_sets import PermissionSets
from .exceptions import NotFoundError
@@ -57,7 +58,7 @@ class TaskOrders(object):
try:
task_order = db.session.query(TaskOrder).filter_by(id=task_order_id).one()
Authorization.check_task_order_permission(
user, task_order, Permissions.VIEW_TASK_ORDER, "view task order"
user, task_order, Permissions.VIEW_TASK_ORDER_DETAILS, "view task order"
)
return task_order
@@ -67,7 +68,7 @@ class TaskOrders(object):
@classmethod
def create(cls, creator, portfolio):
Authorization.check_portfolio_permission(
creator, portfolio, Permissions.UPDATE_TASK_ORDER, "add task order"
creator, portfolio, Permissions.CREATE_TASK_ORDER, "add task order"
)
task_order = TaskOrder(portfolio=portfolio, creator=creator)
@@ -79,7 +80,7 @@ class TaskOrders(object):
@classmethod
def update(cls, user, task_order, **kwargs):
Authorization.check_task_order_permission(
user, task_order, Permissions.UPDATE_TASK_ORDER, "update task order"
user, task_order, Permissions.EDIT_TASK_ORDER_DETAILS, "update task order"
)
for key, value in kwargs.items():
@@ -150,7 +151,7 @@ class TaskOrders(object):
Authorization.check_portfolio_permission(
user,
task_order.portfolio,
Permissions.ADD_TASK_ORDER_OFFICER,
Permissions.EDIT_TASK_ORDER_DETAILS,
"add task order officer",
)
@@ -170,7 +171,12 @@ class TaskOrders(object):
portfolio_user = existing_member.user
else:
member = Portfolios.create_member(
user, portfolio, {**officer_data, "portfolio_role": "officer"}
user,
portfolio,
{
**officer_data,
"permission_sets": [PermissionSets.EDIT_PORTFOLIO_FUNDING],
},
)
portfolio_user = member.user

View File

@@ -4,7 +4,7 @@ from sqlalchemy.exc import IntegrityError
from atst.database import db
from atst.models import User
from .roles import Roles
from .permission_sets import PermissionSets
from .exceptions import NotFoundError, AlreadyExistsError, UnauthorizedError
@@ -29,7 +29,7 @@ class Users(object):
@classmethod
def create(cls, dod_id, atat_role_name=None, **kwargs):
atat_role = Roles.get(atat_role_name)
atat_role = PermissionSets.get(atat_role_name)
try:
user = User(dod_id=dod_id, atat_role=atat_role, **kwargs)
@@ -56,7 +56,7 @@ class Users(object):
def update_role(cls, user_id, atat_role_name):
user = Users.get(user_id)
atat_role = Roles.get(atat_role_name)
atat_role = PermissionSets.get(atat_role_name)
user.atat_role = atat_role
db.session.add(user)

View File

@@ -1,4 +1,3 @@
from atst.domain.roles import PORTFOLIO_ROLES as PORTFOLIO_ROLE_DEFINITIONS
from atst.utils.localization import translate, translate_duration
@@ -107,12 +106,6 @@ COMPLETION_DATE_RANGES = [
("Above 12 months", "Above 12 months"),
]
PORTFOLIO_ROLES = [
(role["name"], {"name": role["display_name"], "description": role["description"]})
for role in PORTFOLIO_ROLE_DEFINITIONS
if role["name"] is not "officer"
]
ENVIRONMENT_ROLES = [
(
"developer",

View File

@@ -1,18 +0,0 @@
from wtforms.validators import Required
from .forms import BaseForm
from atst.forms.fields import SelectField
from atst.utils.localization import translate
from .data import PORTFOLIO_ROLES
class EditMemberForm(BaseForm):
# This form also accepts a field for each environment in each application
# that the user is a member of
portfolio_role = SelectField(
translate("forms.edit_member.portfolio_role_label"),
choices=PORTFOLIO_ROLES,
validators=[Required()],
)

View File

@@ -1,34 +0,0 @@
from wtforms.fields import StringField
from wtforms.fields.html5 import EmailField
from wtforms.validators import Required, Email, Length
from .forms import BaseForm
from atst.forms.validators import IsNumber
from atst.forms.fields import SelectField
from atst.utils.localization import translate
from .data import PORTFOLIO_ROLES
class NewMemberForm(BaseForm):
first_name = StringField(
label=translate("forms.new_member.first_name_label"), validators=[Required()]
)
last_name = StringField(
label=translate("forms.new_member.last_name_label"), validators=[Required()]
)
email = EmailField(
translate("forms.new_member.email_label"), validators=[Required(), Email()]
)
dod_id = StringField(
translate("forms.new_member.dod_id_label"),
validators=[Required(), Length(min=10), IsNumber()],
)
portfolio_role = SelectField(
translate("forms.new_member.portfolio_role_label"),
choices=PORTFOLIO_ROLES,
validators=[Required()],
default="",
description=translate("forms.new_member.portfolio_role_description"),
)

View File

@@ -0,0 +1,72 @@
from wtforms.fields import StringField
from wtforms.fields.html5 import EmailField
from wtforms.validators import Required, Email, Length
from atst.domain.permission_sets import PermissionSets
from .forms import BaseForm
from atst.forms.validators import IsNumber
from atst.forms.fields import SelectField
from atst.utils.localization import translate
class PermissionsForm(BaseForm):
perms_app_mgmt = SelectField(
None,
choices=[
(PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT, "View Only"),
(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT, "Edit Access"),
],
)
perms_funding = SelectField(
None,
choices=[
(PermissionSets.VIEW_PORTFOLIO_FUNDING, "View Only"),
(PermissionSets.EDIT_PORTFOLIO_FUNDING, "Edit Access"),
],
)
perms_reporting = SelectField(
None,
choices=[
(PermissionSets.VIEW_PORTFOLIO_REPORTS, "View Only"),
(PermissionSets.EDIT_PORTFOLIO_REPORTS, "Edit Access"),
],
)
perms_portfolio_mgmt = SelectField(
None,
choices=[
(PermissionSets.VIEW_PORTFOLIO_ADMIN, "View Only"),
(PermissionSets.EDIT_PORTFOLIO_ADMIN, "Edit Access"),
],
)
@property
def data(self):
_data = super().data
_data["permission_sets"] = []
for field in _data:
if "perms" in field:
_data["permission_sets"].append(_data[field])
return _data
class EditForm(PermissionsForm):
# This form also accepts a field for each environment in each application
# that the user is a member of
pass
class NewForm(PermissionsForm):
first_name = StringField(
label=translate("forms.new_member.first_name_label"), validators=[Required()]
)
last_name = StringField(
label=translate("forms.new_member.last_name_label"), validators=[Required()]
)
email = EmailField(
translate("forms.new_member.email_label"), validators=[Required(), Email()]
)
dod_id = StringField(
translate("forms.new_member.dod_id_label"),
validators=[Required(), Length(min=10), IsNumber()],
)

View File

@@ -3,7 +3,7 @@ from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from .permissions import Permissions
from .role import Role
from .permission_set import PermissionSet
from .user import User
from .portfolio_role import PortfolioRole
from .portfolio import Portfolio

View File

@@ -0,0 +1,19 @@
from sqlalchemy import String, Column
from sqlalchemy.dialects.postgresql import ARRAY
from atst.models import Base, types, mixins
class PermissionSet(Base, mixins.TimestampsMixin):
__tablename__ = "permission_sets"
id = types.Id()
name = Column(String, index=True, unique=True, nullable=False)
display_name = Column(String, nullable=False)
description = Column(String, nullable=False)
permissions = Column(ARRAY(String), index=True, server_default="{}", nullable=False)
def __repr__(self):
return "<PermissionSet(name='{}', description='{}', permissions='{}', id='{}')>".format(
self.name, self.description, self.permissions, self.id
)

View File

@@ -1,49 +1,41 @@
class Permissions(object):
VIEW_AUDIT_LOG = "view_audit_log"
VIEW_PORTFOLIO_AUDIT_LOG = "view_portfolio_audit_log"
REQUEST_JEDI_PORTFOLIO = "request_jedi_portfolio"
VIEW_ORIGINAL_JEDI_REQEUST = "view_original_jedi_request"
REVIEW_AND_APPROVE_JEDI_PORTFOLIO_REQUEST = (
"review_and_approve_jedi_portfolio_request"
)
MODIFY_ATAT_ROLE_PERMISSIONS = "modify_atat_role_permissions"
CREATE_CSP_ROLE = "create_csp_role"
DELETE_CSP_ROLE = "delete_csp_role"
DEACTIVE_CSP_ROLE = "deactivate_csp_role"
MODIFY_CSP_ROLE_PERMISSIONS = "modify_csp_role_permissions"
VIEW_USAGE_REPORT = "view_usage_report"
VIEW_USAGE_DOLLARS = "view_usage_dollars"
ADD_AND_ASSIGN_CSP_ROLES = "add_and_assign_csp_roles"
REMOVE_CSP_ROLES = "remove_csp_roles"
REQUEST_NEW_CSP_ROLE = "request_new_csp_role"
ASSIGN_AND_UNASSIGN_ATAT_ROLE = "assign_and_unassign_atat_role"
VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS = "view_assigned_atat_role_configurations"
VIEW_ASSIGNED_CSP_ROLE_CONFIGURATIONS = "view_assigned_csp_role_configurations"
EDIT_PORTFOLIO_INFORMATION = "edit_portfolio_information"
DEACTIVATE_PORTFOLIO = "deactivate_portfolio"
VIEW_ATAT_PERMISSIONS = "view_atat_permissions"
TRANSFER_OWNERSHIP_OF_PORTFOLIO = "transfer_ownership_of_portfolio"
VIEW_PORTFOLIO_MEMBERS = "view_portfolio_members"
# base portfolio perms
VIEW_PORTFOLIO = "view_portfolio"
ADD_APPLICATION_IN_PORTFOLIO = "add_application_in_portfolio"
DELETE_APPLICATION_IN_PORTFOLIO = "delete_application_in_portfolio"
DEACTIVATE_APPLICATION_IN_PORTFOLIO = "deactivate_application_in_portfolio"
VIEW_APPLICATION_IN_PORTFOLIO = "view_application_in_portfolio"
RENAME_APPLICATION_IN_PORTFOLIO = "rename_application_in_portfolio"
# application management
VIEW_APPLICATION = "view_application"
EDIT_APPLICATION = "edit_application"
CREATE_APPLICATION = "create_application"
VIEW_APPLICATION_MEMBER = "view_application_member"
EDIT_APPLICATION_MEMBER = "edit_application_member"
CREATE_APPLICATION_MEMBER = "create_application_member"
VIEW_ENVIRONMENT = "view_environment"
EDIT_ENVIRONMENT = "edit_environment"
CREATE_ENVIRONMENT = "create_environment"
ADD_ENVIRONMENT_IN_APPLICATION = "add_environment_in_application"
DELETE_ENVIRONMENT_IN_APPLICATION = "delete_environment_in_application"
DEACTIVATE_ENVIRONMENT_IN_APPLICATION = "deactivate_environment_in_application"
VIEW_ENVIRONMENT_IN_APPLICATION = "view_environment_in_application"
RENAME_ENVIRONMENT_IN_APPLICATION = "rename_environment_in_application"
# funding
VIEW_PORTFOLIO_FUNDING = "view_portfolio_funding" # TO summary page
CREATE_TASK_ORDER = "create_task_order" # create a new TO
VIEW_TASK_ORDER_DETAILS = "view_task_order_details" # individual TO page
EDIT_TASK_ORDER_DETAILS = (
"edit_task_order_details"
) # edit TO that has not been finalized
ADD_TAG_TO_PORTFOLIO = "add_tag_to_portfolio"
REMOVE_TAG_FROM_PORTFOLIO = "remove_tag_from_portfolio"
# reporting
VIEW_PORTFOLIO_REPORTS = "view_portfolio_reports"
VIEW_TASK_ORDER = "view_task_order"
UPDATE_TASK_ORDER = "update_task_order"
ADD_TASK_ORDER_OFFICER = "add_task_order_officers"
# portfolio admin
VIEW_PORTFOLIO_ADMIN = "view_portfolio_admin"
VIEW_PORTFOLIO_NAME = "view_portfolio_name"
EDIT_PORTFOLIO_NAME = "edit_portfolio_name"
VIEW_PORTFOLIO_USERS = "view_portfolio_users"
EDIT_PORTFOLIO_USERS = "edit_portfolio_users"
CREATE_PORTFOLIO_USERS = "create_portfolio_users"
VIEW_PORTFOLIO_ACTIVITY_LOG = "view_portfolio_activity_log"
VIEW_PORTFOLIO_POC = "view_portfolio_poc"
# portfolio POC
EDIT_PORTFOLIO_POC = "edit_portfolio_poc"
ARCHIVE_PORTFOLIO = "archive_portfolio"

View File

@@ -4,6 +4,7 @@ from itertools import chain
from atst.models import Base, mixins, types
from atst.models.portfolio_role import PortfolioRole, Status as PortfolioRoleStatus
from atst.domain.permission_sets import PermissionSets
from atst.utils import first_or_none
from atst.database import db
@@ -23,7 +24,9 @@ class Portfolio(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
@property
def owner(self):
def _is_portfolio_owner(portfolio_role):
return portfolio_role.role.name == "owner"
return PermissionSets.PORTFOLIO_POC in [
perms_set.name for perms_set in portfolio_role.permission_sets
]
owner = first_or_none(_is_portfolio_owner, self.roles)
return owner.user if owner else None

View File

@@ -1,5 +1,5 @@
from enum import Enum
from sqlalchemy import Index, ForeignKey, Column, Enum as SQLAEnum
from sqlalchemy import Index, ForeignKey, Column, Enum as SQLAEnum, Table
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
@@ -10,7 +10,6 @@ from atst.database import db
from atst.models.environment_role import EnvironmentRole
from atst.models.application import Application
from atst.models.environment import Environment
from atst.models.role import Role
MEMBER_STATUSES = {
@@ -30,6 +29,14 @@ class Status(Enum):
PENDING = "pending"
portfolio_roles_permission_sets = Table(
"portfolio_roles_permission_sets",
Base.metadata,
Column("portfolio_role_id", UUID(as_uuid=True), ForeignKey("portfolio_roles.id")),
Column("permission_set_id", UUID(as_uuid=True), ForeignKey("permission_sets.id")),
)
class PortfolioRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
__tablename__ = "portfolio_roles"
@@ -39,29 +46,32 @@ class PortfolioRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
)
portfolio = relationship("Portfolio", back_populates="roles")
role_id = Column(UUID(as_uuid=True), ForeignKey("roles.id"), nullable=False)
role = relationship("Role")
user_id = Column(
UUID(as_uuid=True), ForeignKey("users.id"), index=True, nullable=False
)
status = Column(SQLAEnum(Status, native_enum=False), default=Status.PENDING)
permission_sets = relationship(
"PermissionSet", secondary=portfolio_roles_permission_sets
)
@property
def permissions(self):
return [
perm for permset in self.permission_sets for perm in permset.permissions
]
def __repr__(self):
return "<PortfolioRole(role='{}', portfolio='{}', user_id='{}', id='{}')>".format(
self.role.name, self.portfolio.name, self.user_id, self.id
return "<PortfolioRole(portfolio='{}', user_id='{}', id='{}', permissions={})>".format(
self.portfolio.name, self.user_id, self.id, self.permissions
)
@property
def history(self):
previous_state = self.get_changes()
change_set = {}
if "role_id" in previous_state:
from_role_id = previous_state["role_id"][0]
from_role = db.session.query(Role).filter(Role.id == from_role_id).one()
to_role = self.role_name
change_set["role"] = [from_role.name, to_role]
# TODO: need to update to include permission_sets
if "status" in previous_state:
from_status = previous_state["status"][0].value
to_status = self.status.value
@@ -105,18 +115,10 @@ class PortfolioRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
def has_dod_id_error(self):
return self.latest_invitation and self.latest_invitation.is_rejected_wrong_user
@property
def role_name(self):
return self.role.name
@property
def user_name(self):
return self.user.full_name
@property
def role_displayname(self):
return self.role.display_name
@property
def is_active(self):
return self.status == Status.ACTIVE

View File

@@ -1,32 +0,0 @@
from sqlalchemy import String, Column
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.orm.attributes import flag_modified
from atst.models import Base, types, mixins
class Role(Base, mixins.TimestampsMixin):
__tablename__ = "roles"
id = types.Id()
name = Column(String, index=True, unique=True, nullable=False)
display_name = Column(String, nullable=False)
description = Column(String, nullable=False)
permissions = Column(ARRAY(String), index=True, server_default="{}", nullable=False)
def add_permission(self, permission):
perms_set = set(self.permissions)
perms_set.add(permission)
self.permissions = list(perms_set)
flag_modified(self, "permissions")
def remove_permission(self, permission):
perms_set = set(self.permissions)
perms_set.discard(permission)
self.permissions = list(perms_set)
flag_modified(self, "permissions")
def __repr__(self):
return "<Role(name='{}', description='{}', permissions='{}', id='{}')>".format(
self.name, self.description, self.permissions, self.id
)

View File

@@ -11,9 +11,9 @@ class User(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
id = types.Id()
username = Column(String)
atat_role_id = Column(UUID(as_uuid=True), ForeignKey("roles.id"))
atat_role_id = Column(UUID(as_uuid=True), ForeignKey("permission_sets.id"))
atat_role = relationship("Role")
atat_role = relationship("PermissionSet")
portfolio_roles = relationship("PortfolioRole", backref="user")
email = Column(String, unique=True)

View File

@@ -64,7 +64,9 @@ def home():
elif num_portfolios == 1:
portfolio_role = user.portfolio_roles[0]
portfolio_id = portfolio_role.portfolio.id
is_portfolio_owner = portfolio_role.role.name == "owner"
is_portfolio_owner = "portfolio_poc" in [
ps.name for ps in portfolio_role.permission_sets
]
if is_portfolio_owner:
return redirect(

View File

@@ -64,7 +64,7 @@ def portfolio_reports(portfolio_id):
Authorization.check_portfolio_permission(
g.current_user,
portfolio,
Permissions.VIEW_USAGE_DOLLARS,
Permissions.VIEW_PORTFOLIO_REPORTS,
"view portfolio reports",
)

View File

@@ -10,13 +10,8 @@ from atst.domain.portfolio_roles import PortfolioRoles, MEMBER_STATUS_CHOICES
from atst.domain.environments import Environments
from atst.domain.environment_roles import EnvironmentRoles
from atst.services.invitation import Invitation as InvitationService
from atst.forms.new_member import NewMemberForm
from atst.forms.edit_member import EditMemberForm
from atst.forms.data import (
ENVIRONMENT_ROLES,
ENV_ROLE_MODAL_DESCRIPTION,
PORTFOLIO_ROLE_DEFINITIONS,
)
import atst.forms.portfolio_member as member_forms
from atst.forms.data import ENVIRONMENT_ROLES, ENV_ROLE_MODAL_DESCRIPTION
from atst.domain.authz import Authorization
from atst.models.permissions import Permissions
@@ -28,7 +23,7 @@ def serialize_portfolio_role(portfolio_role):
"name": portfolio_role.user_name,
"status": portfolio_role.display_status,
"id": portfolio_role.user_id,
"role": portfolio_role.role_displayname,
"role": "admin",
"num_env": portfolio_role.num_environment_roles,
"edit_link": url_for(
"portfolios.view_member",
@@ -46,7 +41,6 @@ def portfolio_members(portfolio_id):
return render_template(
"portfolios/members/index.html",
portfolio=portfolio,
role_choices=PORTFOLIO_ROLE_DEFINITIONS,
status_choices=MEMBER_STATUS_CHOICES,
members=members_list,
)
@@ -70,7 +64,7 @@ def application_members(portfolio_id, application_id):
@portfolios_bp.route("/portfolios/<portfolio_id>/members/new")
def new_member(portfolio_id):
portfolio = Portfolios.get(g.current_user, portfolio_id)
form = NewMemberForm()
form = member_forms.NewForm()
return render_template(
"portfolios/members/new.html", portfolio=portfolio, form=form
)
@@ -79,7 +73,7 @@ def new_member(portfolio_id):
@portfolios_bp.route("/portfolios/<portfolio_id>/members/new", methods=["POST"])
def create_member(portfolio_id):
portfolio = Portfolios.get(g.current_user, portfolio_id)
form = NewMemberForm(http_request.form)
form = member_forms.NewForm(http_request.form)
if form.validate():
try:
@@ -110,12 +104,12 @@ def view_member(portfolio_id, member_id):
Authorization.check_portfolio_permission(
g.current_user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.EDIT_PORTFOLIO_USERS,
"edit this portfolio user",
)
member = PortfolioRoles.get(portfolio_id, member_id)
applications = Applications.get_all(g.current_user, member, portfolio)
form = EditMemberForm(portfolio_role=member.role_name)
form = member_forms.EditForm(portfolio_role="admin")
editable = g.current_user == member.user
can_revoke_access = Portfolios.can_revoke_access_for(portfolio, member)
@@ -144,7 +138,7 @@ def update_member(portfolio_id, member_id):
Authorization.check_portfolio_permission(
g.current_user,
portfolio,
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
Permissions.EDIT_PORTFOLIO_USERS,
"edit this portfolio user",
)
member = PortfolioRoles.get(portfolio_id, member_id)
@@ -157,20 +151,11 @@ def update_member(portfolio_id, member_id):
env_role = form_dict[entry] or None
ids_and_roles.append({"id": env_id, "role": env_role})
form = EditMemberForm(http_request.form)
form = member_forms.EditForm(http_request.form)
if form.validate():
new_role_name = None
if form.data["portfolio_role"] != member.role.name:
member = Portfolios.update_member(
g.current_user, portfolio, member, form.data["portfolio_role"]
)
new_role_name = member.role_displayname
flash(
"portfolio_role_updated",
member_name=member.user_name,
updated_role=new_role_name,
)
member = Portfolios.update_member(
g.current_user, portfolio, member, form.data["permission_sets"]
)
updated_roles = Environments.update_environment_roles(
g.current_user, portfolio, member, ids_and_roles
)