Make portfolio invitation specific to portfolio

- add a base domain class
- extract shared model code to mixin
- rename invitation classes
- invitation model relationship to portfolio_role name is now more
  generic "role"
This commit is contained in:
dandds 2019-04-22 14:54:37 -04:00
parent ac36e34c13
commit c4ad7b4378
20 changed files with 228 additions and 203 deletions

View File

@ -6,8 +6,8 @@ from . import user_can_access
from atst.domain.portfolios import Portfolios from atst.domain.portfolios import Portfolios
from atst.domain.task_orders import TaskOrders from atst.domain.task_orders import TaskOrders
from atst.domain.applications import Applications from atst.domain.applications import Applications
from atst.domain.invitations import Invitations
from atst.domain.environments import Environments from atst.domain.environments import Environments
from atst.domain.invitations import PortfolioInvitations
from atst.domain.exceptions import UnauthorizedError from atst.domain.exceptions import UnauthorizedError
@ -24,8 +24,8 @@ def check_access(permission, message, override, *args, **kwargs):
access_args["portfolio"] = task_order.portfolio access_args["portfolio"] = task_order.portfolio
elif "token" in kwargs: elif "token" in kwargs:
invite = Invitations._get(kwargs["token"]) invite = PortfolioInvitations._get(kwargs["token"])
access_args["portfolio"] = invite.portfolio_role.portfolio access_args["portfolio"] = invite.role.portfolio
elif "portfolio_id" in kwargs: elif "portfolio_id" in kwargs:
access_args["portfolio"] = Portfolios.get( access_args["portfolio"] = Portfolios.get(

View File

@ -2,7 +2,7 @@ import datetime
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
from atst.database import db from atst.database import db
from atst.models.invitation import Invitation, Status as InvitationStatus from atst.models import InvitationStatus, PortfolioInvitation
from atst.domain.portfolio_roles import PortfolioRoles from atst.domain.portfolio_roles import PortfolioRoles
from .exceptions import NotFoundError from .exceptions import NotFoundError
@ -38,27 +38,29 @@ class InvitationError(Exception):
return "{} has a status of {}".format(self.invite.id, self.invite.status.value) return "{} has a status of {}".format(self.invite.id, self.invite.status.value)
class Invitations(object): class BaseInvitations(object):
model = None
# number of minutes a given invitation is considered valid # number of minutes a given invitation is considered valid
EXPIRATION_LIMIT_MINUTES = 360 EXPIRATION_LIMIT_MINUTES = 360
@classmethod @classmethod
def _get(cls, token): def _get(cls, token):
try: try:
invite = db.session.query(Invitation).filter_by(token=token).one() invite = db.session.query(cls.model).filter_by(token=token).one()
except NoResultFound: except NoResultFound:
raise NotFoundError("invite") raise NotFoundError("invite")
return invite return invite
@classmethod @classmethod
def create(cls, inviter, portfolio_role, email): def create(cls, inviter, role, email):
invite = Invitation( # pylint: disable=not-callable
portfolio_role=portfolio_role, invite = cls.model(
role=role,
inviter=inviter, inviter=inviter,
user=portfolio_role.user, user=role.user,
status=InvitationStatus.PENDING, status=InvitationStatus.PENDING,
expiration_time=Invitations.current_expiration_time(), expiration_time=cls.current_expiration_time(),
email=email, email=email,
) )
db.session.add(invite) db.session.add(invite)
@ -68,29 +70,29 @@ class Invitations(object):
@classmethod @classmethod
def accept(cls, user, token): def accept(cls, user, token):
invite = Invitations._get(token) invite = cls._get(token)
if invite.user.dod_id != user.dod_id: if invite.user.dod_id != user.dod_id:
if invite.is_pending: if invite.is_pending:
Invitations._update_status(invite, InvitationStatus.REJECTED_WRONG_USER) cls._update_status(invite, InvitationStatus.REJECTED_WRONG_USER)
raise WrongUserError(user, invite) raise WrongUserError(user, invite)
elif invite.is_expired: elif invite.is_expired:
Invitations._update_status(invite, InvitationStatus.REJECTED_EXPIRED) cls._update_status(invite, InvitationStatus.REJECTED_EXPIRED)
raise ExpiredError(invite) raise ExpiredError(invite)
elif invite.is_accepted or invite.is_revoked or invite.is_rejected: elif invite.is_accepted or invite.is_revoked or invite.is_rejected:
raise InvitationError(invite) raise InvitationError(invite)
elif invite.is_pending: # pragma: no branch elif invite.is_pending: # pragma: no branch
Invitations._update_status(invite, InvitationStatus.ACCEPTED) cls._update_status(invite, InvitationStatus.ACCEPTED)
PortfolioRoles.enable(invite.portfolio_role) PortfolioRoles.enable(invite.role)
return invite return invite
@classmethod @classmethod
def current_expiration_time(cls): def current_expiration_time(cls):
return datetime.datetime.now() + datetime.timedelta( return datetime.datetime.now() + datetime.timedelta(
minutes=Invitations.EXPIRATION_LIMIT_MINUTES minutes=cls.EXPIRATION_LIMIT_MINUTES
) )
@classmethod @classmethod
@ -103,23 +105,25 @@ class Invitations(object):
@classmethod @classmethod
def revoke(cls, token): def revoke(cls, token):
invite = Invitations._get(token) invite = cls._get(token)
return Invitations._update_status(invite, InvitationStatus.REVOKED) return cls._update_status(invite, InvitationStatus.REVOKED)
@classmethod @classmethod
def lookup_by_portfolio_and_user(cls, portfolio, user): def lookup_by_portfolio_and_user(cls, portfolio, user):
portfolio_role = PortfolioRoles.get(portfolio.id, user.id) role = PortfolioRoles.get(portfolio.id, user.id)
if portfolio_role.latest_invitation is None: if role.latest_invitation is None:
raise NotFoundError("invitation") raise NotFoundError("invitation")
return portfolio_role.latest_invitation return role.latest_invitation
@classmethod @classmethod
def resend(cls, user, token): def resend(cls, user, token):
previous_invitation = Invitations._get(token) previous_invitation = cls._get(token)
Invitations._update_status(previous_invitation, InvitationStatus.REVOKED) cls._update_status(previous_invitation, InvitationStatus.REVOKED)
return Invitations.create( return cls.create(user, previous_invitation.role, previous_invitation.email)
user, previous_invitation.portfolio_role, previous_invitation.email
)
class PortfolioInvitations(BaseInvitations):
model = PortfolioInvitation

View File

@ -5,14 +5,16 @@ Base = declarative_base()
from .permissions import Permissions from .permissions import Permissions
from .permission_set import PermissionSet from .permission_set import PermissionSet
from .user import User from .user import User
from .portfolio_role import PortfolioRole from .portfolio_role import PortfolioRole, Status as PortfolioRoleStatus
from .application_role import ApplicationRole from .application_role import ApplicationRole, Status as ApplicationRoleStatus
from .environment_role import EnvironmentRole from .environment_role import EnvironmentRole
from .portfolio import Portfolio from .portfolio import Portfolio
from .application import Application from .application import Application
from .environment import Environment from .environment import Environment
from .attachment import Attachment from .attachment import Attachment
from .audit_event import AuditEvent from .audit_event import AuditEvent
from .invitation import Invitation from .portfolio_invitation import PortfolioInvitation
from .task_order import TaskOrder from .task_order import TaskOrder
from .dd_254 import DD254 from .dd_254 import DD254
from .mixins.invites import Status as InvitationStatus

View File

@ -2,3 +2,4 @@ from .timestamps import TimestampsMixin
from .auditable import AuditableMixin from .auditable import AuditableMixin
from .permissions import PermissionsMixin from .permissions import PermissionsMixin
from .deletable import DeletableMixin from .deletable import DeletableMixin
from .invites import InvitesMixin

View File

@ -3,12 +3,11 @@ from enum import Enum
import secrets import secrets
from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum, TIMESTAMP, String from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum, TIMESTAMP, String
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship, backref from sqlalchemy.orm import relationship
from atst.models import Base, types from atst.models import types
from atst.models.mixins.timestamps import TimestampsMixin
from atst.models.mixins.auditable import AuditableMixin
class Status(Enum): class Status(Enum):
@ -19,24 +18,24 @@ class Status(Enum):
REJECTED_EXPIRED = "rejected_expired" REJECTED_EXPIRED = "rejected_expired"
class Invitation(Base, TimestampsMixin, AuditableMixin): class InvitesMixin(object):
__tablename__ = "invitations"
id = types.Id() id = types.Id()
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True) @declared_attr
user = relationship("User", backref="invitations", foreign_keys=[user_id]) def user_id(cls):
return Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
portfolio_role_id = Column( @declared_attr
UUID(as_uuid=True), ForeignKey("portfolio_roles.id"), index=True def user(cls):
) return relationship("User", foreign_keys=[cls.user_id])
portfolio_role = relationship(
"PortfolioRole",
backref=backref("invitations", order_by="Invitation.time_created"),
)
inviter_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True) @declared_attr
inviter = relationship("User", backref="sent_invites", foreign_keys=[inviter_id]) def inviter_id(cls):
return Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
@declared_attr
def inviter(cls):
return relationship("User", foreign_keys=[cls.inviter_id])
status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING)) status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING))
@ -47,8 +46,9 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
email = Column(String, nullable=False) email = Column(String, nullable=False)
def __repr__(self): def __repr__(self):
return "<Invitation(user='{}', portfolio_role='{}', id='{}', email='{}')>".format( role_id = self.role.id if self.role else None
self.user_id, self.portfolio_role_id, self.id, self.email return "<{}(user='{}', role='{}', id='{}', email='{}')>".format(
self.__class__.__name__, self.user_id, role_id, self.id, self.email
) )
@property @property
@ -90,14 +90,9 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
Status.REVOKED, Status.REVOKED,
] ]
@property
def portfolio(self):
if self.portfolio_role: # pragma: no branch
return self.portfolio_role.portfolio
@property @property
def user_name(self): def user_name(self):
return self.portfolio_role.user.full_name return self.role.user.full_name
@property @property
def is_revokable(self): def is_revokable(self):
@ -110,21 +105,3 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
@property @property
def user_dod_id(self): def user_dod_id(self):
return self.user.dod_id if self.user is not None else None return self.user.dod_id if self.user is not None else None
@property
def event_details(self):
return {"email": self.email, "dod_id": self.user_dod_id}
@property
def history(self):
changes = self.get_changes()
change_set = {}
if "status" in changes:
change_set["status"] = [s.name for s in changes["status"]]
return change_set
@property
def portfolio_id(self):
return self.portfolio_role.portfolio_id

View File

@ -0,0 +1,41 @@
from sqlalchemy import Column, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship, backref
from atst.models import Base
from atst.models.mixins import TimestampsMixin, AuditableMixin, InvitesMixin
class PortfolioInvitation(Base, TimestampsMixin, AuditableMixin, InvitesMixin):
__tablename__ = "invitations"
portfolio_role_id = Column(
UUID(as_uuid=True), ForeignKey("portfolio_roles.id"), index=True
)
role = relationship(
"PortfolioRole",
backref=backref("invitations", order_by="PortfolioInvitation.time_created"),
)
@property
def portfolio(self):
if self.role: # pragma: no branch
return self.role.portfolio
@property
def portfolio_id(self):
return self.role.portfolio_id
@property
def event_details(self):
return {"email": self.email, "dod_id": self.user_dod_id}
@property
def history(self):
changes = self.get_changes()
change_set = {}
if "status" in changes:
change_set["status"] = [s.name for s in changes["status"]]
return change_set

View File

@ -4,6 +4,7 @@ from sqlalchemy.dialects.postgresql import UUID
from atst.models import Base, types, mixins from atst.models import Base, types, mixins
from atst.models.permissions import Permissions from atst.models.permissions import Permissions
from atst.models.portfolio_invitation import PortfolioInvitation
users_permission_sets = Table( users_permission_sets = Table(
@ -31,6 +32,13 @@ class User(
primaryjoin="and_(ApplicationRole.user_id==User.id, ApplicationRole.deleted==False)", primaryjoin="and_(ApplicationRole.user_id==User.id, ApplicationRole.deleted==False)",
) )
portfolio_invitations = relationship(
"PortfolioInvitation", foreign_keys=PortfolioInvitation.user_id
)
sent_portfolio_invitations = relationship(
"PortfolioInvitation", foreign_keys=PortfolioInvitation.inviter_id
)
email = Column(String) email = Column(String)
dod_id = Column(String, unique=True, nullable=False) dod_id = Column(String, unique=True, nullable=False)
first_name = Column(String) first_name = Column(String)

View File

@ -1,7 +1,7 @@
from flask import g, redirect, url_for, render_template from flask import g, redirect, url_for, render_template
from . import portfolios_bp from . import portfolios_bp
from atst.domain.invitations import Invitations from atst.domain.invitations import PortfolioInvitations
from atst.queue import queue from atst.queue import queue
from atst.utils.flash import formatted_flash as flash from atst.utils.flash import formatted_flash as flash
from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.domain.authz.decorator import user_can_access_decorator as user_can
@ -19,7 +19,7 @@ def send_invite_email(owner_name, token, new_member_email):
@portfolios_bp.route("/portfolios/invitations/<token>", methods=["GET"]) @portfolios_bp.route("/portfolios/invitations/<token>", methods=["GET"])
def accept_invitation(token): def accept_invitation(token):
invite = Invitations.accept(g.current_user, token) invite = PortfolioInvitations.accept(g.current_user, token)
for task_order in invite.portfolio.task_orders: for task_order in invite.portfolio.task_orders:
if g.current_user in task_order.officers: if g.current_user in task_order.officers:
@ -37,7 +37,7 @@ def accept_invitation(token):
) )
@user_can(Permissions.EDIT_PORTFOLIO_USERS, message="revoke invitation") @user_can(Permissions.EDIT_PORTFOLIO_USERS, message="revoke invitation")
def revoke_invitation(portfolio_id, token): def revoke_invitation(portfolio_id, token):
Invitations.revoke(token) PortfolioInvitations.revoke(token)
return redirect( return redirect(
url_for( url_for(
@ -54,7 +54,7 @@ def revoke_invitation(portfolio_id, token):
) )
@user_can(Permissions.EDIT_PORTFOLIO_USERS, message="resend invitation") @user_can(Permissions.EDIT_PORTFOLIO_USERS, message="resend invitation")
def resend_invitation(portfolio_id, token): def resend_invitation(portfolio_id, token):
invite = Invitations.resend(g.current_user, token) invite = PortfolioInvitations.resend(g.current_user, token)
send_invite_email(g.current_user.full_name, invite.token, invite.email) send_invite_email(g.current_user.full_name, invite.token, invite.email)
flash("resend_portfolio_invitation", user_name=invite.user_name) flash("resend_portfolio_invitation", user_name=invite.user_name)
return redirect( return redirect(

View File

@ -7,7 +7,7 @@ from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.models.permissions import Permissions from atst.models.permissions import Permissions
from atst.database import db from atst.database import db
from atst.domain.exceptions import NotFoundError, NoAccessError from atst.domain.exceptions import NotFoundError, NoAccessError
from atst.domain.invitations import Invitations from atst.domain.invitations import PortfolioInvitations
from atst.domain.portfolios import Portfolios from atst.domain.portfolios import Portfolios
from atst.utils.localization import translate from atst.utils.localization import translate
from atst.forms.officers import EditTaskOrderOfficersForm from atst.forms.officers import EditTaskOrderOfficersForm
@ -57,7 +57,7 @@ def resend_invite(task_order_id):
if not officer: if not officer:
raise NotFoundError("officer") raise NotFoundError("officer")
invitation = Invitations.lookup_by_portfolio_and_user(portfolio, officer) invitation = PortfolioInvitations.lookup_by_portfolio_and_user(portfolio, officer)
if not invitation: if not invitation:
raise NotFoundError("invitation") raise NotFoundError("invitation")
@ -65,11 +65,11 @@ def resend_invite(task_order_id):
if not invitation.can_resend: if not invitation.can_resend:
raise NoAccessError("invitation") raise NoAccessError("invitation")
Invitations.revoke(token=invitation.token) PortfolioInvitations.revoke(token=invitation.token)
invite_service = InvitationService( invite_service = InvitationService(
g.current_user, g.current_user,
invitation.portfolio_role, invitation.role,
invitation.email, invitation.email,
subject=invite_type_info["subject"], subject=invite_type_info["subject"],
email_template=invite_type_info["template"], email_template=invite_type_info["template"],

View File

@ -1,6 +1,6 @@
from flask import render_template from flask import render_template
from atst.domain.invitations import Invitations from atst.domain.invitations import PortfolioInvitations
from atst.queue import queue from atst.queue import queue
from atst.domain.task_orders import TaskOrders from atst.domain.task_orders import TaskOrders
from atst.domain.portfolio_roles import PortfolioRoles from atst.domain.portfolio_roles import PortfolioRoles
@ -68,7 +68,7 @@ class Invitation:
return invite return invite
def _create_invite(self): def _create_invite(self):
return Invitations.create(self.inviter, self.member, self.email) return PortfolioInvitations.create(self.inviter, self.member, self.email)
def _send_invite_email(self, token): def _send_invite_email(self, token):
body = render_template( body = render_template(

View File

@ -3,33 +3,32 @@ import pytest
import re import re
from atst.domain.invitations import ( from atst.domain.invitations import (
Invitations, PortfolioInvitations,
InvitationError, InvitationError,
WrongUserError, WrongUserError,
ExpiredError, ExpiredError,
NotFoundError, NotFoundError,
) )
from atst.models.invitation import Status from atst.domain.audit_log import AuditLog
from atst.models import InvitationStatus
from tests.factories import ( from tests.factories import (
PortfolioFactory, PortfolioFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
UserFactory, UserFactory,
InvitationFactory, PortfolioInvitationFactory,
) )
from atst.domain.audit_log import AuditLog
def test_create_invitation(): def test_create_invitation():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
assert invite.user == user assert invite.user == user
assert invite.portfolio_role == ws_role assert invite.role == ws_role
assert invite.inviter == portfolio.owner assert invite.inviter == portfolio.owner
assert invite.status == Status.PENDING assert invite.status == InvitationStatus.PENDING
assert re.match(r"^[\w\-_]+$", invite.token) assert re.match(r"^[\w\-_]+$", invite.token)
@ -37,9 +36,9 @@ def test_accept_invitation():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
assert invite.is_pending assert invite.is_pending
accepted_invite = Invitations.accept(user, invite.token) accepted_invite = PortfolioInvitations.accept(user, invite.token)
assert accepted_invite.is_accepted assert accepted_invite.is_accepted
@ -47,16 +46,16 @@ def test_accept_expired_invitation():
user = UserFactory.create() user = UserFactory.create()
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
increment = Invitations.EXPIRATION_LIMIT_MINUTES + 1 increment = PortfolioInvitations.EXPIRATION_LIMIT_MINUTES + 1
expiration_time = datetime.datetime.now() - datetime.timedelta(minutes=increment) expiration_time = datetime.datetime.now() - datetime.timedelta(minutes=increment)
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user=user, user=user,
expiration_time=expiration_time, expiration_time=expiration_time,
status=Status.PENDING, status=InvitationStatus.PENDING,
portfolio_role=ws_role, role=ws_role,
) )
with pytest.raises(ExpiredError): with pytest.raises(ExpiredError):
Invitations.accept(user, invite.token) PortfolioInvitations.accept(user, invite.token)
assert invite.is_rejected assert invite.is_rejected
@ -65,22 +64,22 @@ def test_accept_rejected_invite():
user = UserFactory.create() user = UserFactory.create()
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user=user, status=Status.REJECTED_EXPIRED, portfolio_role=ws_role user=user, status=InvitationStatus.REJECTED_EXPIRED, role=ws_role
) )
with pytest.raises(InvitationError): with pytest.raises(InvitationError):
Invitations.accept(user, invite.token) PortfolioInvitations.accept(user, invite.token)
def test_accept_revoked_invite(): def test_accept_revoked_invite():
user = UserFactory.create() user = UserFactory.create()
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user=user, status=Status.REVOKED, portfolio_role=ws_role user=user, status=InvitationStatus.REVOKED, role=ws_role
) )
with pytest.raises(InvitationError): with pytest.raises(InvitationError):
Invitations.accept(user, invite.token) PortfolioInvitations.accept(user, invite.token)
def test_wrong_user_accepts_invitation(): def test_wrong_user_accepts_invitation():
@ -88,9 +87,9 @@ def test_wrong_user_accepts_invitation():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
wrong_user = UserFactory.create() wrong_user = UserFactory.create()
invite = InvitationFactory.create(user=user, portfolio_role=ws_role) invite = PortfolioInvitationFactory.create(user=user, role=ws_role)
with pytest.raises(WrongUserError): with pytest.raises(WrongUserError):
Invitations.accept(wrong_user, invite.token) PortfolioInvitations.accept(wrong_user, invite.token)
def test_user_cannot_accept_invitation_accepted_by_wrong_user(): def test_user_cannot_accept_invitation_accepted_by_wrong_user():
@ -98,30 +97,30 @@ def test_user_cannot_accept_invitation_accepted_by_wrong_user():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
wrong_user = UserFactory.create() wrong_user = UserFactory.create()
invite = InvitationFactory.create(user=user, portfolio_role=ws_role) invite = PortfolioInvitationFactory.create(user=user, role=ws_role)
with pytest.raises(WrongUserError): with pytest.raises(WrongUserError):
Invitations.accept(wrong_user, invite.token) PortfolioInvitations.accept(wrong_user, invite.token)
with pytest.raises(InvitationError): with pytest.raises(InvitationError):
Invitations.accept(user, invite.token) PortfolioInvitations.accept(user, invite.token)
def test_accept_invitation_twice(): def test_accept_invitation_twice():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
Invitations.accept(user, invite.token) PortfolioInvitations.accept(user, invite.token)
with pytest.raises(InvitationError): with pytest.raises(InvitationError):
Invitations.accept(user, invite.token) PortfolioInvitations.accept(user, invite.token)
def test_revoke_invitation(): def test_revoke_invitation():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
assert invite.is_pending assert invite.is_pending
Invitations.revoke(invite.token) PortfolioInvitations.revoke(invite.token)
assert invite.is_revoked assert invite.is_revoked
@ -129,8 +128,8 @@ def test_resend_invitation():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
Invitations.resend(user, invite.token) PortfolioInvitations.resend(user, invite.token)
assert ws_role.invitations[0].is_revoked assert ws_role.invitations[0].is_revoked
assert ws_role.invitations[1].is_pending assert ws_role.invitations[1].is_pending
@ -139,8 +138,8 @@ def test_audit_event_for_accepted_invite():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
invite = Invitations.accept(user, invite.token) invite = PortfolioInvitations.accept(user, invite.token)
accepted_event = AuditLog.get_by_resource(invite.id)[0] accepted_event = AuditLog.get_by_resource(invite.id)[0]
assert "email" in accepted_event.event_details assert "email" in accepted_event.event_details
@ -151,9 +150,11 @@ def test_lookup_by_user_and_portfolio():
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
user = UserFactory.create() user = UserFactory.create()
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio) ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = Invitations.create(portfolio.owner, ws_role, user.email) invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
assert Invitations.lookup_by_portfolio_and_user(portfolio, user) == invite assert PortfolioInvitations.lookup_by_portfolio_and_user(portfolio, user) == invite
with pytest.raises(NotFoundError): with pytest.raises(NotFoundError):
Invitations.lookup_by_portfolio_and_user(portfolio, UserFactory.create()) PortfolioInvitations.lookup_by_portfolio_and_user(
portfolio, UserFactory.create()
)

View File

@ -7,7 +7,7 @@ from atst.models.portfolio_role import Status as PortfolioRoleStatus
from tests.factories import ( from tests.factories import (
PortfolioFactory, PortfolioFactory,
UserFactory, UserFactory,
InvitationFactory, PortfolioInvitationFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
) )

View File

@ -7,11 +7,8 @@ import datetime
from atst.forms import data from atst.forms import data
from atst.models import * from atst.models import *
from atst.models.portfolio_role import Status as PortfolioRoleStatus
from atst.models.application_role import Status as ApplicationRoleStatus from atst.domain.invitations import PortfolioInvitations
from atst.models.invitation import Status as InvitationStatus
from atst.models.environment_role import CSPRole
from atst.domain.invitations import Invitations
from atst.domain.permission_sets import PermissionSets from atst.domain.permission_sets import PermissionSets
from atst.domain.portfolio_roles import PortfolioRoles from atst.domain.portfolio_roles import PortfolioRoles
@ -240,13 +237,13 @@ class EnvironmentRoleFactory(Base):
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
class InvitationFactory(Base): class PortfolioInvitationFactory(Base):
class Meta: class Meta:
model = Invitation model = PortfolioInvitation
email = factory.Faker("email") email = factory.Faker("email")
status = InvitationStatus.PENDING status = InvitationStatus.PENDING
expiration_time = Invitations.current_expiration_time() expiration_time = PortfolioInvitations.current_expiration_time()
class AttachmentFactory(Base): class AttachmentFactory(Base):

View File

@ -1,11 +1,9 @@
import pytest
import datetime import datetime
from atst.models.invitation import Invitation, Status from atst.models import InvitationStatus, PortfolioRoleStatus
from atst.models.portfolio_role import Status as PortfolioRoleStatus
from tests.factories import ( from tests.factories import (
InvitationFactory, PortfolioInvitationFactory,
PortfolioFactory, PortfolioFactory,
UserFactory, UserFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
@ -18,9 +16,9 @@ def test_expired_invite_is_not_revokable():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
expiration_time=datetime.datetime.now() - datetime.timedelta(minutes=60), expiration_time=datetime.datetime.now() - datetime.timedelta(minutes=60),
portfolio_role=portfolio_role, role=portfolio_role,
) )
assert not invite.is_revokable assert not invite.is_revokable
@ -31,7 +29,7 @@ def test_unexpired_invite_is_revokable():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create(portfolio_role=portfolio_role) invite = PortfolioInvitationFactory.create(role=portfolio_role)
assert invite.is_revokable assert invite.is_revokable
@ -41,7 +39,7 @@ def test_invite_is_not_revokable_if_invite_is_not_pending():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, status=Status.ACCEPTED role=portfolio_role, status=InvitationStatus.ACCEPTED
) )
assert not invite.is_revokable assert not invite.is_revokable

View File

@ -6,14 +6,11 @@ from atst.domain.portfolios import Portfolios
from atst.domain.portfolio_roles import PortfolioRoles from atst.domain.portfolio_roles import PortfolioRoles
from atst.domain.applications import Applications from atst.domain.applications import Applications
from atst.domain.permission_sets import PermissionSets from atst.domain.permission_sets import PermissionSets
from atst.models.portfolio_role import Status from atst.models import AuditEvent, InvitationStatus, PortfolioRoleStatus, CSPRole
from atst.models.invitation import Status as InvitationStatus
from atst.models.audit_event import AuditEvent
from atst.models.portfolio_role import Status as PortfolioRoleStatus
from atst.models.environment_role import CSPRole
from tests.factories import ( from tests.factories import (
UserFactory, UserFactory,
InvitationFactory, PortfolioInvitationFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
EnvironmentFactory, EnvironmentFactory,
EnvironmentRoleFactory, EnvironmentRoleFactory,
@ -189,12 +186,12 @@ def test_has_environment_roles():
def test_status_when_member_is_active(): def test_status_when_member_is_active():
portfolio_role = PortfolioRoleFactory.create(status=Status.ACTIVE) portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.ACTIVE)
assert portfolio_role.display_status == "Active" assert portfolio_role.display_status == "Active"
def test_status_when_member_is_disabled(): def test_status_when_member_is_disabled():
portfolio_role = PortfolioRoleFactory.create(status=Status.DISABLED) portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.DISABLED)
assert portfolio_role.display_status == "Disabled" assert portfolio_role.display_status == "Disabled"
@ -204,8 +201,8 @@ def test_status_when_invitation_has_been_rejected_for_expirations():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, status=InvitationStatus.REJECTED_EXPIRED role=portfolio_role, status=InvitationStatus.REJECTED_EXPIRED
) )
assert portfolio_role.display_status == "Invite expired" assert portfolio_role.display_status == "Invite expired"
@ -216,8 +213,8 @@ def test_status_when_invitation_has_been_rejected_for_wrong_user():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, status=InvitationStatus.REJECTED_WRONG_USER role=portfolio_role, status=InvitationStatus.REJECTED_WRONG_USER
) )
assert portfolio_role.display_status == "Error on invite" assert portfolio_role.display_status == "Error on invite"
@ -228,8 +225,8 @@ def test_status_when_invitation_has_been_revoked():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, status=InvitationStatus.REVOKED role=portfolio_role, status=InvitationStatus.REVOKED
) )
assert portfolio_role.display_status == "Invite revoked" assert portfolio_role.display_status == "Invite revoked"
@ -240,8 +237,8 @@ def test_status_when_invitation_is_expired():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, role=portfolio_role,
status=InvitationStatus.PENDING, status=InvitationStatus.PENDING,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1), expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
) )
@ -254,8 +251,8 @@ def test_can_not_resend_invitation_if_active():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, status=InvitationStatus.ACCEPTED role=portfolio_role, status=InvitationStatus.ACCEPTED
) )
assert not portfolio_role.can_resend_invitation assert not portfolio_role.can_resend_invitation
@ -266,8 +263,8 @@ def test_can_resend_invitation_if_expired():
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
portfolio_role=portfolio_role, status=InvitationStatus.REJECTED_EXPIRED role=portfolio_role, status=InvitationStatus.REJECTED_EXPIRED
) )
assert portfolio_role.can_resend_invitation assert portfolio_role.can_resend_invitation

View File

@ -6,12 +6,11 @@ from tests.factories import (
UserFactory, UserFactory,
PortfolioFactory, PortfolioFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
InvitationFactory, PortfolioInvitationFactory,
TaskOrderFactory, TaskOrderFactory,
) )
from atst.domain.portfolios import Portfolios from atst.domain.portfolios import Portfolios
from atst.models.portfolio_role import Status as PortfolioRoleStatus from atst.models import InvitationStatus, PortfolioRoleStatus
from atst.models.invitation import Status as InvitationStatus
from atst.domain.users import Users from atst.domain.users import Users
from atst.domain.permission_sets import PermissionSets from atst.domain.permission_sets import PermissionSets
@ -22,7 +21,7 @@ def test_existing_member_accepts_valid_invite(client, user_session):
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create(user_id=user.id, portfolio_role=ws_role) invite = PortfolioInvitationFactory.create(user_id=user.id, role=ws_role)
# the user does not have access to the portfolio before accepting the invite # the user does not have access to the portfolio before accepting the invite
assert len(Portfolios.for_user(user)) == 0 assert len(Portfolios.for_user(user)) == 0
@ -60,7 +59,7 @@ def test_new_member_accepts_valid_invite(monkeypatch, client, user_session):
assert response.status_code == 302 assert response.status_code == 302
user = Users.get_by_dod_id(user_info["dod_id"]) user = Users.get_by_dod_id(user_info["dod_id"])
token = user.invitations[0].token token = user.portfolio_invitations[0].token
monkeypatch.setattr( monkeypatch.setattr(
"atst.domain.auth.should_redirect_to_user_profile", lambda *args: False "atst.domain.auth.should_redirect_to_user_profile", lambda *args: False
@ -84,10 +83,8 @@ def test_member_accepts_invalid_invite(client, user_session):
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, user_id=user.id, role=ws_role, status=InvitationStatus.REJECTED_WRONG_USER
portfolio_role=ws_role,
status=InvitationStatus.REJECTED_WRONG_USER,
) )
user_session(user) user_session(user)
response = client.get(url_for("portfolios.accept_invitation", token=invite.token)) response = client.get(url_for("portfolios.accept_invitation", token=invite.token))
@ -119,7 +116,7 @@ def test_user_accepts_invite_with_wrong_dod_id(client, user_session):
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create(user_id=user.id, portfolio_role=ws_role) invite = PortfolioInvitationFactory.create(user_id=user.id, role=ws_role)
user_session(different_user) user_session(different_user)
response = client.get(url_for("portfolios.accept_invitation", token=invite.token)) response = client.get(url_for("portfolios.accept_invitation", token=invite.token))
@ -132,9 +129,9 @@ def test_user_accepts_expired_invite(client, user_session):
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, user_id=user.id,
portfolio_role=ws_role, role=ws_role,
status=InvitationStatus.REJECTED_EXPIRED, status=InvitationStatus.REJECTED_EXPIRED,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1), expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
) )
@ -150,9 +147,9 @@ def test_revoke_invitation(client, user_session):
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, user_id=user.id,
portfolio_role=ws_role, role=ws_role,
status=InvitationStatus.REJECTED_EXPIRED, status=InvitationStatus.REJECTED_EXPIRED,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1), expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
) )
@ -176,9 +173,9 @@ def test_user_can_only_revoke_invites_in_their_portfolio(client, user_session):
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
user=user, portfolio=other_portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=other_portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, user_id=user.id,
portfolio_role=portfolio_role, role=portfolio_role,
status=InvitationStatus.REJECTED_EXPIRED, status=InvitationStatus.REJECTED_EXPIRED,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1), expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
) )
@ -202,9 +199,9 @@ def test_user_can_only_resend_invites_in_their_portfolio(client, user_session, q
portfolio_role = PortfolioRoleFactory.create( portfolio_role = PortfolioRoleFactory.create(
user=user, portfolio=other_portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=other_portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, user_id=user.id,
portfolio_role=portfolio_role, role=portfolio_role,
status=InvitationStatus.REJECTED_EXPIRED, status=InvitationStatus.REJECTED_EXPIRED,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1), expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
) )
@ -227,8 +224,8 @@ def test_resend_invitation_sends_email(client, user_session, queue):
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, portfolio_role=ws_role, status=InvitationStatus.PENDING user_id=user.id, role=ws_role, status=InvitationStatus.PENDING
) )
user_session(portfolio.owner) user_session(portfolio.owner)
client.post( client.post(
@ -250,9 +247,9 @@ def test_existing_member_invite_resent_to_email_submitted_in_form(
ws_role = PortfolioRoleFactory.create( ws_role = PortfolioRoleFactory.create(
user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING user=user, portfolio=portfolio, status=PortfolioRoleStatus.PENDING
) )
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
user_id=user.id, user_id=user.id,
portfolio_role=ws_role, role=ws_role,
status=InvitationStatus.PENDING, status=InvitationStatus.PENDING,
email="example@example.com", email="example@example.com",
) )
@ -290,7 +287,7 @@ def test_contracting_officer_accepts_invite(monkeypatch, client, user_session):
# contracting officer accepts invitation # contracting officer accepts invitation
user = Users.get_by_dod_id(user_info["dod_id"]) user = Users.get_by_dod_id(user_info["dod_id"])
token = user.invitations[0].token token = user.portfolio_invitations[0].token
monkeypatch.setattr( monkeypatch.setattr(
"atst.domain.auth.should_redirect_to_user_profile", lambda *args: False "atst.domain.auth.should_redirect_to_user_profile", lambda *args: False
) )
@ -324,7 +321,7 @@ def test_cor_accepts_invite(monkeypatch, client, user_session):
# contracting officer representative accepts invitation # contracting officer representative accepts invitation
user = Users.get_by_dod_id(user_info["dod_id"]) user = Users.get_by_dod_id(user_info["dod_id"])
token = user.invitations[0].token token = user.portfolio_invitations[0].token
monkeypatch.setattr( monkeypatch.setattr(
"atst.domain.auth.should_redirect_to_user_profile", lambda *args: False "atst.domain.auth.should_redirect_to_user_profile", lambda *args: False
) )
@ -358,7 +355,7 @@ def test_so_accepts_invite(monkeypatch, client, user_session):
# security officer accepts invitation # security officer accepts invitation
user = Users.get_by_dod_id(user_info["dod_id"]) user = Users.get_by_dod_id(user_info["dod_id"])
token = user.invitations[0].token token = user.portfolio_invitations[0].token
monkeypatch.setattr( monkeypatch.setattr(
"atst.domain.auth.should_redirect_to_user_profile", lambda *args: False "atst.domain.auth.should_redirect_to_user_profile", lambda *args: False
) )

View File

@ -45,7 +45,7 @@ def test_create_member(client, user_session):
assert response.status_code == 200 assert response.status_code == 200
assert user.full_name in response.data.decode() assert user.full_name in response.data.decode()
assert user.has_portfolios assert user.has_portfolios
assert user.invitations assert user.portfolio_invitations
assert len(queue.get_queue()) == queue_length + 1 assert len(queue.get_queue()) == queue_length + 1
portfolio_role = user.portfolio_roles[0] portfolio_role = user.portfolio_roles[0]
assert len(portfolio_role.permission_sets) == 5 assert len(portfolio_role.permission_sets) == 5

View File

@ -4,7 +4,7 @@ from flask import url_for
import pytest import pytest
from atst.domain.task_orders import TaskOrders from atst.domain.task_orders import TaskOrders
from atst.models.invitation import Status as InvitationStatus from atst.models import InvitationStatus
from atst.models.portfolio_role import Status as PortfolioStatus from atst.models.portfolio_role import Status as PortfolioStatus
from atst.queue import queue from atst.queue import queue
@ -13,7 +13,7 @@ from tests.factories import (
TaskOrderFactory, TaskOrderFactory,
UserFactory, UserFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
InvitationFactory, PortfolioInvitationFactory,
) )
@ -79,7 +79,7 @@ def test_does_not_resend_officer_invitation(client, user_session):
user_session(user) user_session(user)
for i in range(2): for i in range(2):
client.post(url_for("task_orders.invite", task_order_id=task_order.id)) client.post(url_for("task_orders.invite", task_order_id=task_order.id))
assert len(contracting_officer.invitations) == 1 assert len(contracting_officer.portfolio_invitations) == 1
def test_does_not_invite_if_task_order_incomplete(client, user_session, queue): def test_does_not_invite_if_task_order_incomplete(client, user_session, queue):
@ -272,9 +272,9 @@ class TestTaskOrderInvitations:
cor_invite=True, cor_invite=True,
) )
portfolio_role = PortfolioRoleFactory.create(portfolio=self.portfolio, user=cor) portfolio_role = PortfolioRoleFactory.create(portfolio=self.portfolio, user=cor)
invitation = InvitationFactory.create( PortfolioInvitationFactory.create(
inviter=self.portfolio.owner, inviter=self.portfolio.owner,
portfolio_role=portfolio_role, role=portfolio_role,
user=cor, user=cor,
status=InvitationStatus.PENDING, status=InvitationStatus.PENDING,
) )
@ -369,9 +369,9 @@ def test_resend_invite_when_not_pending(app, client, user_session, portfolio, us
portfolio=portfolio, user=user, status=PortfolioStatus.ACTIVE portfolio=portfolio, user=user, status=PortfolioStatus.ACTIVE
) )
original_invitation = InvitationFactory.create( original_invitation = PortfolioInvitationFactory.create(
inviter=user, inviter=user,
portfolio_role=portfolio_role, role=portfolio_role,
email=user.email, email=user.email,
status=InvitationStatus.ACCEPTED, status=InvitationStatus.ACCEPTED,
) )
@ -397,9 +397,9 @@ def test_resending_revoked_invite(app, client, user_session, portfolio, user):
portfolio_role = PortfolioRoleFactory.create(portfolio=portfolio, user=user) portfolio_role = PortfolioRoleFactory.create(portfolio=portfolio, user=user)
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
inviter=user, inviter=user,
portfolio_role=portfolio_role, role=portfolio_role,
email=user.email, email=user.email,
status=InvitationStatus.REVOKED, status=InvitationStatus.REVOKED,
) )
@ -427,9 +427,9 @@ def test_resending_expired_invite(app, client, user_session, portfolio):
portfolio=portfolio, contracting_officer=ko, ko_invite=True portfolio=portfolio, contracting_officer=ko, ko_invite=True
) )
portfolio_role = PortfolioRoleFactory.create(portfolio=portfolio, user=ko) portfolio_role = PortfolioRoleFactory.create(portfolio=portfolio, user=ko)
invite = InvitationFactory.create( invite = PortfolioInvitationFactory.create(
inviter=portfolio.owner, inviter=portfolio.owner,
portfolio_role=portfolio_role, role=portfolio_role,
email=ko.email, email=ko.email,
expiration_time=datetime.now() - timedelta(days=1), expiration_time=datetime.now() - timedelta(days=1),
) )

View File

@ -10,5 +10,5 @@ def test_invite_member(queue):
ws_member = PortfolioRoleFactory.create(user=new_member, portfolio=portfolio) ws_member = PortfolioRoleFactory.create(user=new_member, portfolio=portfolio)
invite_service = Invitation(inviter, ws_member, new_member.email) invite_service = Invitation(inviter, ws_member, new_member.email)
new_invitation = invite_service.invite() new_invitation = invite_service.invite()
assert new_invitation == new_member.invitations[0] assert new_invitation == new_member.portfolio_invitations[0]
assert len(queue.get_queue()) == 1 assert len(queue.get_queue()) == 1

View File

@ -17,7 +17,7 @@ from tests.factories import (
ApplicationRoleFactory, ApplicationRoleFactory,
EnvironmentFactory, EnvironmentFactory,
EnvironmentRoleFactory, EnvironmentRoleFactory,
InvitationFactory, PortfolioInvitationFactory,
PortfolioFactory, PortfolioFactory,
PortfolioRoleFactory, PortfolioRoleFactory,
TaskOrderFactory, TaskOrderFactory,
@ -75,7 +75,9 @@ def test_all_protected_routes_have_access_control(
monkeypatch.setattr("atst.domain.portfolios.Portfolios.get", lambda *a: None) monkeypatch.setattr("atst.domain.portfolios.Portfolios.get", lambda *a: None)
monkeypatch.setattr("atst.domain.task_orders.TaskOrders.get", lambda *a: Mock()) monkeypatch.setattr("atst.domain.task_orders.TaskOrders.get", lambda *a: Mock())
monkeypatch.setattr("atst.domain.applications.Applications.get", lambda *a: Mock()) monkeypatch.setattr("atst.domain.applications.Applications.get", lambda *a: Mock())
monkeypatch.setattr("atst.domain.invitations.Invitations._get", lambda *a: Mock()) monkeypatch.setattr(
"atst.domain.invitations.PortfolioInvitations._get", lambda *a: Mock()
)
monkeypatch.setattr( monkeypatch.setattr(
"atst.utils.context_processors.get_portfolio_from_context", lambda *a: None "atst.utils.context_processors.get_portfolio_from_context", lambda *a: None
) )
@ -402,7 +404,7 @@ def test_portfolios_resend_invitation_access(post_url_assert_status):
portfolio = PortfolioFactory.create(owner=owner) portfolio = PortfolioFactory.create(owner=owner)
prr = PortfolioRoleFactory.create(user=invitee, portfolio=portfolio) prr = PortfolioRoleFactory.create(user=invitee, portfolio=portfolio)
invite = InvitationFactory.create(user=UserFactory.create(), portfolio_role=prr) invite = PortfolioInvitationFactory.create(user=UserFactory.create(), role=prr)
url = url_for( url = url_for(
"portfolios.resend_invitation", portfolio_id=portfolio.id, token=invite.token "portfolios.resend_invitation", portfolio_id=portfolio.id, token=invite.token
@ -423,7 +425,7 @@ def test_task_orders_resend_invite_access(post_url_assert_status):
portfolio = PortfolioFactory.create(owner=owner) portfolio = PortfolioFactory.create(owner=owner)
task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko) task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko)
prr = PortfolioRoleFactory.create(user=ko, portfolio=portfolio) prr = PortfolioRoleFactory.create(user=ko, portfolio=portfolio)
invite = InvitationFactory.create(user=UserFactory.create(), portfolio_role=prr) PortfolioInvitationFactory.create(user=UserFactory.create(), role=prr)
url = url_for( url = url_for(
"task_orders.resend_invite", "task_orders.resend_invite",
@ -449,7 +451,7 @@ def test_portfolios_revoke_invitation_access(post_url_assert_status):
prr = PortfolioRoleFactory.create( prr = PortfolioRoleFactory.create(
user=prt_member, portfolio=portfolio, status=PortfolioRoleStatus.ACTIVE user=prt_member, portfolio=portfolio, status=PortfolioRoleStatus.ACTIVE
) )
invite = InvitationFactory.create(user=prt_member, portfolio_role=prr) invite = PortfolioInvitationFactory.create(user=prt_member, role=prr)
url = url_for( url = url_for(
"portfolios.revoke_invitation", "portfolios.revoke_invitation",
portfolio_id=portfolio.id, portfolio_id=portfolio.id,