diff --git a/atst/routes/applications/index.py b/atst/routes/applications/index.py index d61417f4..3aebb6b1 100644 --- a/atst/routes/applications/index.py +++ b/atst/routes/applications/index.py @@ -5,7 +5,21 @@ from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.models.permissions import Permissions +def has_portfolio_applications(_user, portfolio=None, **_kwargs): + """ + If the portfolio exists and the user has access to applications + within the scoped portfolio, the user has access to the + portfolio landing page. + """ + if portfolio and portfolio.applications: + return True + + @applications_bp.route("/portfolios//applications") -@user_can(Permissions.VIEW_APPLICATION, message="view portfolio applications") +@user_can( + Permissions.VIEW_APPLICATION, + override=has_portfolio_applications, + message="view portfolio applications", +) def portfolio_applications(portfolio_id): return render_template("portfolios/applications/index.html") diff --git a/atst/routes/applications/invitations.py b/atst/routes/applications/invitations.py index a3893da8..3a7c80c3 100644 --- a/atst/routes/applications/invitations.py +++ b/atst/routes/applications/invitations.py @@ -10,6 +10,7 @@ def accept_invitation(token): return redirect( url_for( - "portfolios.show_portfolio", portfolio_id=invite.application.portfolio_id + "applications.portfolio_applications", + portfolio_id=invite.application.portfolio_id, ) ) diff --git a/templates/emails/application/invitation.txt b/templates/emails/application/invitation.txt index 1d49b452..18771021 100644 --- a/templates/emails/application/invitation.txt +++ b/templates/emails/application/invitation.txt @@ -5,6 +5,6 @@ Join this JEDI Cloud Application {{ owner }} has invited you to join a JEDI Cloud Application. Login now to view or use your JEDI Cloud resources. -{# url_for("application.accept_invitation", token=token, _external=True) #} +{{ url_for("applications.accept_invitation", token=token, _external=True) }} {% endblock %} diff --git a/tests/routes/applications/test_index.py b/tests/routes/applications/test_index.py index b3e8dc9a..614d8d89 100644 --- a/tests/routes/applications/test_index.py +++ b/tests/routes/applications/test_index.py @@ -1,13 +1,6 @@ from flask import url_for, get_flashed_messages -from tests.factories import ( - UserFactory, - PortfolioFactory, - PortfolioRoleFactory, - EnvironmentRoleFactory, - EnvironmentFactory, - ApplicationFactory, -) +from tests.factories import * from atst.domain.applications import Applications from atst.domain.portfolios import Portfolios @@ -68,3 +61,31 @@ def test_user_without_permission_has_no_add_application_link(client, user_sessio url_for("applications.create", portfolio_id=portfolio.id) not in response.data.decode() ) + + +def test_portfolio_applications_user_with_application_roles(client, user_session): + user = UserFactory.create() + portfolio = PortfolioFactory.create() + + app1 = ApplicationFactory.create(portfolio=portfolio, name="X-Wing") + app2 = ApplicationFactory.create(portfolio=portfolio, name="TIE Fighter") + app3 = ApplicationFactory.create(portfolio=portfolio, name="Millenium Falcon") + + ApplicationRoleFactory.create( + application=app1, user=user, status=ApplicationRoleStatus.ACTIVE + ) + ApplicationRoleFactory.create( + application=app2, user=user, status=ApplicationRoleStatus.ACTIVE + ) + + user_session(user) + response = client.get( + url_for("applications.portfolio_applications", portfolio_id=portfolio.id) + ) + assert response.status_code == 200 + + body = response.data.decode() + + assert app1.name in body + assert app2.name in body + assert app3.name not in body diff --git a/tests/routes/applications/test_invitations.py b/tests/routes/applications/test_invitations.py index 718485dd..9ca6d23b 100644 --- a/tests/routes/applications/test_invitations.py +++ b/tests/routes/applications/test_invitations.py @@ -16,8 +16,26 @@ def test_accept_application_invitation(client, user_session): assert response.status_code == 302 expected_location = url_for( - "portfolios.show_portfolio", + "applications.portfolio_applications", portfolio_id=application.portfolio_id, _external=True, ) assert response.location == expected_location + + +def test_accept_application_invitation_end_to_end(client, user_session): + user = UserFactory.create() + application = ApplicationFactory.create(name="Millenium Falcon") + app_role = ApplicationRoleFactory.create(application=application, user=user) + invite = ApplicationInvitationFactory.create( + role=app_role, user=user, inviter=application.portfolio.owner + ) + + user_session(user) + response = client.get( + url_for("applications.accept_invitation", token=invite.token), + follow_redirects=True, + ) + + assert response.status_code == 200 + assert application.name in response.data.decode() diff --git a/tests/test_access.py b/tests/test_access.py index 2767ef28..41f8f297 100644 --- a/tests/test_access.py +++ b/tests/test_access.py @@ -8,8 +8,7 @@ import atst from atst.app import make_app, make_config from atst.domain.auth import UNPROTECTED_ROUTES as _NO_LOGIN_REQUIRED from atst.domain.permission_sets import PermissionSets -from atst.models.environment_role import CSPRole -from atst.models.portfolio_role import Status as PortfolioRoleStatus +from atst.models import CSPRole, PortfolioRoleStatus, ApplicationRoleStatus from tests.factories import ( AttachmentFactory, @@ -361,12 +360,18 @@ def test_portfolios_admin_access(get_url_assert_status): def test_applications_portfolio_applications_access(get_url_assert_status): ccpo = user_with(PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT) owner = user_with() + app_user = user_with() rando = user_with() portfolio = PortfolioFactory.create(owner=owner) + application = ApplicationFactory.create(portfolio=portfolio) + ApplicationRoleFactory.create( + application=application, user=app_user, status=ApplicationRoleStatus.ACTIVE + ) url = url_for("applications.portfolio_applications", portfolio_id=portfolio.id) get_url_assert_status(ccpo, url, 200) get_url_assert_status(owner, url, 200) + get_url_assert_status(app_user, url, 200) get_url_assert_status(rando, url, 404)