Merge pull request #721 from dod-ccpo/app-perms-bug

User can only update apps or revoke invites related to their portfolios
This commit is contained in:
leigh-mil 2019-03-26 14:24:04 -04:00 committed by GitHub
commit cd999f52ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 267 additions and 5 deletions

View File

@ -5,21 +5,31 @@ from flask import g, current_app as app, request
from . import user_can_access
from atst.domain.portfolios import Portfolios
from atst.domain.task_orders import TaskOrders
from atst.domain.applications import Applications
from atst.domain.invitations import Invitations
from atst.domain.exceptions import UnauthorizedError
def check_access(permission, message, exception, *args, **kwargs):
access_args = {"message": message}
if "portfolio_id" in kwargs:
if "application_id" in kwargs:
application = Applications.get(kwargs["application_id"])
access_args["portfolio"] = application.portfolio
elif "task_order_id" in kwargs:
task_order = TaskOrders.get(kwargs["task_order_id"])
access_args["portfolio"] = task_order.portfolio
elif "token" in kwargs:
invite = Invitations._get(kwargs["token"])
access_args["portfolio"] = invite.portfolio_role.portfolio
elif "portfolio_id" in kwargs:
access_args["portfolio"] = Portfolios.get(
g.current_user, kwargs["portfolio_id"]
)
if "task_order_id" in kwargs:
task_order = TaskOrders.get(kwargs["task_order_id"])
access_args["portfolio"] = task_order.portfolio
if exception is not None and exception(g.current_user, **access_args, **kwargs):
return True

View File

@ -157,6 +157,54 @@ def test_user_without_permission_cannot_update_application(client, user_session)
assert application.description == "Cool stuff happening here!"
def test_user_can_only_access_apps_in_their_portfolio(client, user_session):
portfolio = PortfolioFactory.create()
other_portfolio = PortfolioFactory.create(
applications=[
{
"name": "Awesome Application",
"description": "More cool stuff happening here!",
"environments": [{"name": "dev"}],
}
]
)
other_application = other_portfolio.applications[0]
user_session(portfolio.owner)
# user can't view application edit form
response = client.get(
url_for(
"portfolios.edit_application",
portfolio_id=portfolio.id,
application_id=other_application.id,
)
)
assert response.status_code == 404
# user can't post update application form
time_updated = other_application.time_updated
response = client.post(
url_for(
"portfolios.update_application",
portfolio_id=portfolio.id,
application_id=other_application.id,
),
data={"name": "New Name", "description": "A new description."},
)
assert response.status_code == 404
assert time_updated == other_application.time_updated
# user can't view application members
response = client.get(
url_for(
"portfolios.application_members",
portfolio_id=portfolio.id,
application_id=other_application.id,
)
)
assert response.status_code == 404
def create_environment(user):
portfolio = PortfolioFactory.create()
portfolio_role = PortfolioRoleFactory.create(portfolio=portfolio, user=user)

View File

@ -169,6 +169,58 @@ def test_revoke_invitation(client, user_session):
assert invite.is_revoked
def test_user_can_only_revoke_invites_in_their_portfolio(client, user_session):
portfolio = PortfolioFactory.create()
other_portfolio = PortfolioFactory.create()
user = UserFactory.create()
portfolio_role = PortfolioRoleFactory.create(
user=user, portfolio=other_portfolio, status=PortfolioRoleStatus.PENDING
)
invite = InvitationFactory.create(
user_id=user.id,
portfolio_role=portfolio_role,
status=InvitationStatus.REJECTED_EXPIRED,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
)
user_session(portfolio.owner)
response = client.post(
url_for(
"portfolios.revoke_invitation",
portfolio_id=portfolio.id,
token=invite.token,
)
)
assert response.status_code == 404
assert not invite.is_revoked
def test_user_can_only_resend_invites_in_their_portfolio(client, user_session, queue):
portfolio = PortfolioFactory.create()
other_portfolio = PortfolioFactory.create()
user = UserFactory.create()
portfolio_role = PortfolioRoleFactory.create(
user=user, portfolio=other_portfolio, status=PortfolioRoleStatus.PENDING
)
invite = InvitationFactory.create(
user_id=user.id,
portfolio_role=portfolio_role,
status=InvitationStatus.REJECTED_EXPIRED,
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
)
user_session(portfolio.owner)
response = client.post(
url_for(
"portfolios.resend_invitation",
portfolio_id=portfolio.id,
token=invite.token,
)
)
assert response.status_code == 404
assert len(queue.get_queue()) == 0
def test_resend_invitation_sends_email(client, user_session, queue):
user = UserFactory.create()
portfolio = PortfolioFactory.create()

View File

@ -128,6 +128,20 @@ class TestPortfolioFunding:
assert context["funding_end_date"] is expiring_to.end_date
assert context["funded"] == False
def test_user_can_only_access_to_in_their_portfolio(
self, app, user_session, portfolio
):
other_task_order = TaskOrderFactory.create()
user_session(portfolio.owner)
response = app.test_client().get(
url_for(
"portfolios.view_task_order",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
)
)
assert response.status_code == 404
class TestTaskOrderInvitations:
def setup(self):
@ -227,6 +241,54 @@ class TestTaskOrderInvitations:
assert len(queue.get_queue()) == queue_length
assert response.status_code == 400
def test_user_can_only_invite_to_task_order_in_their_portfolio(
self, user_session, client, portfolio
):
other_task_order = TaskOrderFactory.create()
user_session(portfolio.owner)
# user can't see invites
response = client.get(
url_for(
"portfolios.task_order_invitations",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
)
)
assert response.status_code == 404
# user can't send invites
time_updated = other_task_order.time_updated
response = client.post(
url_for(
"portfolios.edit_task_order_invitations",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
),
data={
"contracting_officer-first_name": "Luke",
"contracting_officer-last_name": "Skywalker",
"contracting_officer-dod_id": "0123456789",
"contracting_officer-email": "luke@skywalker.mil",
"contracting_officer-phone_number": "0123456789",
"contracting_officer-invite": "y",
},
)
assert response.status_code == 404
assert time_updated == other_task_order.time_updated
# user can't resend invites
response = client.post(
url_for(
"portfolios.resend_invite",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
invite_type="ko_invite",
)
)
assert response.status_code == 404
assert time_updated == other_task_order.time_updated
def test_ko_can_view_task_order(client, user_session, portfolio, user):
PortfolioRoleFactory.create(
@ -464,6 +526,57 @@ def test_submit_completed_ko_review_page_as_ko(
assert task_order.loas == loa_list
def test_ko_can_only_access_their_to(app, user_session, client, portfolio, pdf_upload):
ko = UserFactory.create()
PortfolioRoleFactory.create(
portfolio=portfolio,
user=ko,
status=PortfolioStatus.ACTIVE,
permission_sets=[
PermissionSets.get(PermissionSets.VIEW_PORTFOLIO),
PermissionSets.get(PermissionSets.VIEW_PORTFOLIO_FUNDING),
],
)
task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko)
dd_254 = DD254Factory.create()
TaskOrders.add_dd_254(task_order, dd_254.to_dictionary())
other_task_order = TaskOrderFactory.create()
user_session(ko)
# KO can't see TO
response = client.get(
url_for(
"portfolios.ko_review",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
)
)
assert response.status_code == 404
# KO can't submit review for TO
form_data = {
"start_date": "02/10/2019",
"end_date": "03/10/2019",
"number": "1938745981",
"loas-0": "1231231231",
"custom_clauses": "hi im a custom clause",
"pdf": pdf_upload,
}
response = client.post(
url_for(
"portfolios.submit_ko_review",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
),
data=form_data,
)
assert response.status_code == 404
assert not TaskOrders.is_signed_by_ko(other_task_order)
def test_so_review_page(app, client, user_session, portfolio):
so = UserFactory.create()
PortfolioRoleFactory.create(
@ -541,6 +654,45 @@ def test_submit_so_review(app, client, user_session, portfolio):
assert task_order.dd_254.certifying_official == dd_254_data["certifying_official"]
def test_so_can_only_access_their_to(app, client, user_session, portfolio):
so = UserFactory.create()
PortfolioRoleFactory.create(
portfolio=portfolio,
user=so,
status=PortfolioStatus.ACTIVE,
permission_sets=[
PermissionSets.get(PermissionSets.VIEW_PORTFOLIO),
PermissionSets.get(PermissionSets.VIEW_PORTFOLIO_FUNDING),
],
)
task_order = TaskOrderFactory.create(portfolio=portfolio, security_officer=so)
dd_254_data = DD254Factory.dictionary()
other_task_order = TaskOrderFactory.create()
user_session(so)
# SO can't view dd254
response = client.get(
url_for(
"portfolios.so_review",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
)
)
assert response.status_code == 404
# SO can't submit dd254
response = client.post(
url_for(
"portfolios.submit_so_review",
portfolio_id=portfolio.id,
task_order_id=other_task_order.id,
),
data=dd_254_data,
)
assert response.status_code == 404
assert not other_task_order.dd_254
def test_resend_invite_when_invalid_invite_officer(
app, client, user_session, portfolio, user
):