diff --git a/alembic/versions/32438a35cfb5_add_application_roles.py b/alembic/versions/32438a35cfb5_add_application_roles.py new file mode 100644 index 00000000..1d4de573 --- /dev/null +++ b/alembic/versions/32438a35cfb5_add_application_roles.py @@ -0,0 +1,59 @@ +"""add application_roles + +Revision ID: 32438a35cfb5 +Revises: 49e12ae7c9ca +Create Date: 2019-04-04 06:49:57.693753 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '32438a35cfb5' +down_revision = '49e12ae7c9ca' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('application_roles', + sa.Column('time_created', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), + sa.Column('time_updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), server_default=sa.text('uuid_generate_v4()'), nullable=False), + sa.Column('application_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('status', sa.Enum('ACTIVE', 'DISABLED', 'PENDING', name='status', native_enum=False), nullable=True), + sa.ForeignKeyConstraint(['application_id'], ['applications.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index('application_role_user_application', 'application_roles', ['user_id', 'application_id'], unique=True) + op.create_index(op.f('ix_application_roles_application_id'), 'application_roles', ['application_id'], unique=False) + op.create_index(op.f('ix_application_roles_user_id'), 'application_roles', ['user_id'], unique=False) + op.create_table('application_roles_permission_sets', + sa.Column('application_role_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('permission_set_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.ForeignKeyConstraint(['application_role_id'], ['application_roles.id'], ), + sa.ForeignKeyConstraint(['permission_set_id'], ['permission_sets.id'], ) + ) + op.create_index(op.f('ix_permission_sets_name'), 'permission_sets', ['name'], unique=True) + op.create_index(op.f('ix_permission_sets_permissions'), 'permission_sets', ['permissions'], unique=False) + op.drop_index('ix_roles_name', table_name='permission_sets') + op.drop_index('ix_roles_permissions', table_name='permission_sets') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_index('ix_roles_permissions', 'permission_sets', ['permissions'], unique=False) + op.create_index('ix_roles_name', 'permission_sets', ['name'], unique=True) + op.drop_index(op.f('ix_permission_sets_permissions'), table_name='permission_sets') + op.drop_index(op.f('ix_permission_sets_name'), table_name='permission_sets') + op.drop_table('application_roles_permission_sets') + op.drop_index(op.f('ix_application_roles_user_id'), table_name='application_roles') + op.drop_index(op.f('ix_application_roles_application_id'), table_name='application_roles') + op.drop_index('application_role_user_application', table_name='application_roles') + op.drop_table('application_roles') + # ### end Alembic commands ### diff --git a/atst/models/__init__.py b/atst/models/__init__.py index bf8dc338..ea00ff74 100644 --- a/atst/models/__init__.py +++ b/atst/models/__init__.py @@ -14,3 +14,4 @@ from .audit_event import AuditEvent from .invitation import Invitation from .task_order import TaskOrder from .dd_254 import DD254 +from .application_role import ApplicationRole diff --git a/atst/models/application.py b/atst/models/application.py index 050b13d7..d3088ebf 100644 --- a/atst/models/application.py +++ b/atst/models/application.py @@ -16,6 +16,7 @@ class Application(Base, mixins.TimestampsMixin, mixins.AuditableMixin): portfolio_id = Column(ForeignKey("portfolios.id"), nullable=False) portfolio = relationship("Portfolio") environments = relationship("Environment", back_populates="application") + roles = relationship("ApplicationRole") @property def users(self): diff --git a/atst/models/application_role.py b/atst/models/application_role.py new file mode 100644 index 00000000..dc0db4d1 --- /dev/null +++ b/atst/models/application_role.py @@ -0,0 +1,58 @@ +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 atst.models import Base, mixins +from .types import Id + + +class Status(Enum): + ACTIVE = "active" + DISABLED = "disabled" + PENDING = "pending" + + +application_roles_permission_sets = Table( + "application_roles_permission_sets", + Base.metadata, + Column( + "application_role_id", UUID(as_uuid=True), ForeignKey("application_roles.id") + ), + Column("permission_set_id", UUID(as_uuid=True), ForeignKey("permission_sets.id")), +) + + +class ApplicationRole( + Base, mixins.TimestampsMixin, mixins.AuditableMixin, mixins.PermissionsMixin +): + __tablename__ = "application_roles" + + id = Id() + application_id = Column( + UUID(as_uuid=True), ForeignKey("applications.id"), index=True, nullable=False + ) + application = relationship("Application", back_populates="roles") + + 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=application_roles_permission_sets + ) + + def __repr__(self): + return "".format( + self.application.name, self.user_id, self.id, self.permissions + ) + + +Index( + "application_role_user_application", + ApplicationRole.user_id, + ApplicationRole.application_id, + unique=True, +)