@@ -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
|
||||
|
||||
19
atst/models/permission_set.py
Normal file
19
atst/models/permission_set.py
Normal 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
|
||||
)
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user