Updates to workspace role permissions, invitations

This commit is contained in:
dandds
2018-10-30 15:22:07 -04:00
parent 4255dbe292
commit 848bbf9c12
16 changed files with 232 additions and 201 deletions

View File

@@ -3,6 +3,7 @@ from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models.invitation import Invitation, Status as InvitationStatus
from atst.domain.workspace_users import WorkspaceUsers
from .exceptions import NotFoundError
@@ -30,9 +31,9 @@ class Invitations(object):
return invite
@classmethod
def create(cls, workspace, inviter, user):
def create(cls, workspace_role, inviter, user):
invite = Invitation(
workspace=workspace,
workspace_role=workspace_role,
inviter=inviter,
user=user,
status=InvitationStatus.PENDING,
@@ -43,20 +44,6 @@ class Invitations(object):
return invite
@classmethod
def create_for_owner(cls, workspace, user):
invite = Invitation(
workspace=workspace,
inviter=user,
user=user,
status=InvitationStatus.ACCEPTED,
expiration_time=Invitations.current_expiration_time(),
)
db.session.add(invite)
db.session.commit()
return invite
@classmethod
def accept(cls, token):
invite = Invitations._get(token)
@@ -72,6 +59,8 @@ class Invitations(object):
if invite.is_revoked or invite.is_rejected:
raise InvitationError(invite)
WorkspaceUsers.enable(invite.workspace_role)
return invite
@classmethod

View File

@@ -1,10 +1,9 @@
from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models.workspace_role import WorkspaceRole
from atst.models.workspace_role import WorkspaceRole, Status as WorkspaceRoleStatus
from atst.models.workspace_user import WorkspaceUser
from atst.models.user import User
from atst.models.invitation import Invitation, Status as InvitationStatus
from .roles import Roles
from .users import Users
@@ -38,9 +37,7 @@ class WorkspaceUsers(object):
db.session.query(WorkspaceRole)
.join(User)
.filter(User.id == user_id, WorkspaceRole.workspace_id == workspace_id)
.join(Invitation, WorkspaceRole.workspace_id == Invitation.workspace_id)
.filter(Invitation.user_id == WorkspaceRole.user_id)
.filter(Invitation.status == InvitationStatus.ACCEPTED)
.filter(WorkspaceRole.status == WorkspaceRoleStatus.ACTIVE)
.one()
)
except NoResultFound:
@@ -150,3 +147,10 @@ class WorkspaceUsers(object):
db.session.commit()
return workspace_users
@classmethod
def enable(cls, workspace_role):
workspace_role.status = WorkspaceRoleStatus.ACTIVE
db.session.add(workspace_role)
db.session.commit()

View File

@@ -4,8 +4,7 @@ from atst.database import db
from atst.domain.common import Query
from atst.domain.exceptions import NotFoundError
from atst.models.workspace import Workspace
from atst.models.workspace_role import WorkspaceRole
from atst.models.invitation import Invitation, Status as InvitationStatus
from atst.models.workspace_role import WorkspaceRole, Status as WorkspaceRoleStatus
class WorkspacesQuery(Query):
@@ -25,10 +24,8 @@ class WorkspacesQuery(Query):
return (
db.session.query(Workspace)
.join(WorkspaceRole)
.join(Invitation)
.filter(WorkspaceRole.user == user)
.filter(Invitation.user == user)
.filter(Invitation.status == InvitationStatus.ACCEPTED)
.filter(WorkspaceRole.status == WorkspaceRoleStatus.ACTIVE)
.all()
)

View File

@@ -3,7 +3,7 @@ from atst.domain.authz import Authorization
from atst.models.permissions import Permissions
from atst.domain.users import Users
from atst.domain.workspace_users import WorkspaceUsers
from atst.domain.invitations import Invitations
from atst.models.workspace_role import Status as WorkspaceRoleStatus
from .query import WorkspacesQuery
from .scopes import ScopedWorkspace
@@ -14,8 +14,9 @@ class Workspaces(object):
def create(cls, request, name=None):
name = name or request.displayname
workspace = WorkspacesQuery.create(request=request, name=name)
Workspaces._create_workspace_role(request.creator, workspace, "owner")
Invitations.create_for_owner(workspace, request.creator)
Workspaces._create_workspace_role(
request.creator, workspace, "owner", status=WorkspaceRoleStatus.ACTIVE
)
WorkspacesQuery.add_and_commit(workspace)
return workspace
@@ -109,9 +110,13 @@ class Workspaces(object):
return WorkspaceUsers.update_role(member, workspace.id, role_name)
@classmethod
def _create_workspace_role(cls, user, workspace, role_name):
def _create_workspace_role(
cls, user, workspace, role_name, status=WorkspaceRoleStatus.PENDING
):
role = Roles.get(role_name)
workspace_role = WorkspacesQuery.create_workspace_role(user, role, workspace)
workspace_role = WorkspacesQuery.create_workspace_role(
user, role, workspace, status=status
)
WorkspacesQuery.add_and_commit(workspace_role)
return workspace_role

View File

@@ -25,8 +25,10 @@ class Invitation(Base, TimestampsMixin):
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
user = relationship("User", backref="invitations", foreign_keys=[user_id])
workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id"), index=True)
workspace = relationship("Workspace", backref="invitations")
workspace_role_id = Column(
UUID(as_uuid=True), ForeignKey("workspace_roles.id"), index=True
)
workspace_role = relationship("WorkspaceRole", backref="invitations")
inviter_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
inviter = relationship("User", backref="sent_invites", foreign_keys=[inviter_id])
@@ -61,3 +63,8 @@ class Invitation(Base, TimestampsMixin):
@property
def is_expired(self):
return datetime.datetime.now(self.expiration_time.tzinfo) > self.expiration_time
@property
def workspace(self):
if self.workspace_role:
return self.workspace_role.workspace

View File

@@ -1,4 +1,5 @@
from sqlalchemy import Index, ForeignKey, Column
from enum import Enum
from sqlalchemy import Index, ForeignKey, Column, Enum as SQLAEnum
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
@@ -6,6 +7,12 @@ from atst.models import Base, mixins
from .types import Id
class Status(Enum):
ACTIVE = "active"
DISABLED = "disabled"
PENDING = "pending"
class WorkspaceRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
__tablename__ = "workspace_roles"
@@ -22,6 +29,8 @@ class WorkspaceRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
UUID(as_uuid=True), ForeignKey("users.id"), index=True, nullable=False
)
status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING))
def __repr__(self):
return "<WorkspaceRole(role='{}', workspace='{}', user_id='{}', id='{}')>".format(
self.role.name, self.workspace.name, self.user_id, self.id

View File

@@ -237,7 +237,9 @@ def create_member(workspace_id):
if form.validate():
try:
new_member = Workspaces.create_member(g.current_user, workspace, form.data)
invite = Invitations.create(workspace, g.current_user, new_member.user)
invite = Invitations.create(
new_member.workspace_role, g.current_user, new_member.user
)
send_invite_email(
g.current_user.full_name, invite.token, new_member.user.email
)