Application users should have access to portfolio landing page.
- Adds override to portfolio landing page access check to see if user has access to any applications within the portfolio. - Route for accepting an application invitation redirects directly to portfolio applications route. - Tests ensure application user only sees apps the user has access to on the portfolio landing page.
This commit is contained in:
parent
8bd945d0d4
commit
59a02572ea
@ -5,7 +5,21 @@ 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
|
||||||
|
|
||||||
|
|
||||||
|
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/<portfolio_id>/applications")
|
@applications_bp.route("/portfolios/<portfolio_id>/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):
|
def portfolio_applications(portfolio_id):
|
||||||
return render_template("portfolios/applications/index.html")
|
return render_template("portfolios/applications/index.html")
|
||||||
|
@ -10,6 +10,7 @@ def accept_invitation(token):
|
|||||||
|
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"portfolios.show_portfolio", portfolio_id=invite.application.portfolio_id
|
"applications.portfolio_applications",
|
||||||
|
portfolio_id=invite.application.portfolio_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
Join this JEDI Cloud Application
|
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.
|
{{ 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 %}
|
{% endblock %}
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
from flask import url_for, get_flashed_messages
|
from flask import url_for, get_flashed_messages
|
||||||
|
|
||||||
from tests.factories import (
|
from tests.factories import *
|
||||||
UserFactory,
|
|
||||||
PortfolioFactory,
|
|
||||||
PortfolioRoleFactory,
|
|
||||||
EnvironmentRoleFactory,
|
|
||||||
EnvironmentFactory,
|
|
||||||
ApplicationFactory,
|
|
||||||
)
|
|
||||||
|
|
||||||
from atst.domain.applications import Applications
|
from atst.domain.applications import Applications
|
||||||
from atst.domain.portfolios import Portfolios
|
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)
|
url_for("applications.create", portfolio_id=portfolio.id)
|
||||||
not in response.data.decode()
|
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
|
||||||
|
@ -16,8 +16,26 @@ def test_accept_application_invitation(client, user_session):
|
|||||||
|
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
expected_location = url_for(
|
expected_location = url_for(
|
||||||
"portfolios.show_portfolio",
|
"applications.portfolio_applications",
|
||||||
portfolio_id=application.portfolio_id,
|
portfolio_id=application.portfolio_id,
|
||||||
_external=True,
|
_external=True,
|
||||||
)
|
)
|
||||||
assert response.location == expected_location
|
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()
|
||||||
|
@ -8,8 +8,7 @@ import atst
|
|||||||
from atst.app import make_app, make_config
|
from atst.app import make_app, make_config
|
||||||
from atst.domain.auth import UNPROTECTED_ROUTES as _NO_LOGIN_REQUIRED
|
from atst.domain.auth import UNPROTECTED_ROUTES as _NO_LOGIN_REQUIRED
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
from atst.models.environment_role import CSPRole
|
from atst.models import CSPRole, PortfolioRoleStatus, ApplicationRoleStatus
|
||||||
from atst.models.portfolio_role import Status as PortfolioRoleStatus
|
|
||||||
|
|
||||||
from tests.factories import (
|
from tests.factories import (
|
||||||
AttachmentFactory,
|
AttachmentFactory,
|
||||||
@ -361,12 +360,18 @@ def test_portfolios_admin_access(get_url_assert_status):
|
|||||||
def test_applications_portfolio_applications_access(get_url_assert_status):
|
def test_applications_portfolio_applications_access(get_url_assert_status):
|
||||||
ccpo = user_with(PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT)
|
ccpo = user_with(PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT)
|
||||||
owner = user_with()
|
owner = user_with()
|
||||||
|
app_user = user_with()
|
||||||
rando = user_with()
|
rando = user_with()
|
||||||
portfolio = PortfolioFactory.create(owner=owner)
|
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)
|
url = url_for("applications.portfolio_applications", portfolio_id=portfolio.id)
|
||||||
get_url_assert_status(ccpo, url, 200)
|
get_url_assert_status(ccpo, url, 200)
|
||||||
get_url_assert_status(owner, url, 200)
|
get_url_assert_status(owner, url, 200)
|
||||||
|
get_url_assert_status(app_user, url, 200)
|
||||||
get_url_assert_status(rando, url, 404)
|
get_url_assert_status(rando, url, 404)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user