Route for accepting an application invitation.
- Domain method for enabling an application role. - Updated ApplicationRole model `history` property so that it serializes the `status` correctly
This commit is contained in:
parent
d8771accca
commit
a2ebdf78a0
@ -1,5 +1,5 @@
|
|||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.models.application_role import ApplicationRole
|
from atst.models import ApplicationRole, ApplicationRoleStatus
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
|
|
||||||
|
|
||||||
@ -21,3 +21,10 @@ class ApplicationRoles(object):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return application_role
|
return application_role
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def enable(cls, role):
|
||||||
|
role.status = ApplicationRoleStatus.ACTIVE
|
||||||
|
|
||||||
|
db.session.add(role)
|
||||||
|
db.session.commit()
|
||||||
|
@ -62,7 +62,13 @@ class ApplicationRole(
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def history(self):
|
def history(self):
|
||||||
return self.get_changes()
|
previous_state = self.get_changes()
|
||||||
|
change_set = {}
|
||||||
|
if "status" in previous_state:
|
||||||
|
from_status = previous_state["status"][0].value
|
||||||
|
to_status = self.status.value
|
||||||
|
change_set["status"] = [from_status, to_status]
|
||||||
|
return change_set
|
||||||
|
|
||||||
def has_permission_set(self, perm_set_name):
|
def has_permission_set(self, perm_set_name):
|
||||||
return first_or_none(
|
return first_or_none(
|
||||||
|
@ -6,6 +6,7 @@ from . import index
|
|||||||
from . import new
|
from . import new
|
||||||
from . import settings
|
from . import settings
|
||||||
from . import team
|
from . import team
|
||||||
|
from . import invitations
|
||||||
from atst.domain.environment_roles import EnvironmentRoles
|
from atst.domain.environment_roles import EnvironmentRoles
|
||||||
from atst.domain.exceptions import UnauthorizedError
|
from atst.domain.exceptions import UnauthorizedError
|
||||||
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
|
||||||
|
15
atst/routes/applications/invitations.py
Normal file
15
atst/routes/applications/invitations.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from flask import redirect, url_for, g
|
||||||
|
|
||||||
|
from . import applications_bp
|
||||||
|
from atst.domain.invitations import ApplicationInvitations
|
||||||
|
|
||||||
|
|
||||||
|
@applications_bp.route("/applications/invitations/<token>", methods=["GET"])
|
||||||
|
def accept_invitation(token):
|
||||||
|
invite = ApplicationInvitations.accept(g.current_user, token)
|
||||||
|
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"portfolios.show_portfolio", portfolio_id=invite.application.portfolio_id
|
||||||
|
)
|
||||||
|
)
|
@ -1,6 +1,8 @@
|
|||||||
from atst.domain.application_roles import ApplicationRoles
|
from atst.domain.application_roles import ApplicationRoles
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
from tests.factories import UserFactory, ApplicationFactory
|
from atst.models import ApplicationRoleStatus
|
||||||
|
|
||||||
|
from tests.factories import *
|
||||||
|
|
||||||
|
|
||||||
def test_create_application_role():
|
def test_create_application_role():
|
||||||
@ -18,3 +20,16 @@ def test_create_application_role():
|
|||||||
)
|
)
|
||||||
assert application_role.application == application
|
assert application_role.application == application
|
||||||
assert application_role.user == user
|
assert application_role.user == user
|
||||||
|
|
||||||
|
|
||||||
|
def test_enabled_application_role():
|
||||||
|
application = ApplicationFactory.create()
|
||||||
|
user = UserFactory.create()
|
||||||
|
app_role = ApplicationRoleFactory.create(
|
||||||
|
application=application, user=user, status=ApplicationRoleStatus.DISABLED
|
||||||
|
)
|
||||||
|
assert app_role.status == ApplicationRoleStatus.DISABLED
|
||||||
|
|
||||||
|
ApplicationRoles.enable(app_role)
|
||||||
|
|
||||||
|
assert app_role.status == ApplicationRoleStatus.ACTIVE
|
||||||
|
23
tests/routes/applications/test_invitations.py
Normal file
23
tests/routes/applications/test_invitations.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from flask import url_for
|
||||||
|
|
||||||
|
from tests.factories import *
|
||||||
|
|
||||||
|
|
||||||
|
def test_accept_application_invitation(client, user_session):
|
||||||
|
user = UserFactory.create()
|
||||||
|
application = ApplicationFactory.create()
|
||||||
|
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))
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
expected_location = url_for(
|
||||||
|
"portfolios.show_portfolio",
|
||||||
|
portfolio_id=application.portfolio_id,
|
||||||
|
_external=True,
|
||||||
|
)
|
||||||
|
assert response.location == expected_location
|
@ -35,6 +35,7 @@ _NO_ACCESS_CHECK_REQUIRED = _NO_LOGIN_REQUIRED + [
|
|||||||
"users.user", # available to all users
|
"users.user", # available to all users
|
||||||
"users.update_user", # available to all users
|
"users.update_user", # available to all users
|
||||||
"portfolios.accept_invitation", # available to all users; access control is built into invitation logic
|
"portfolios.accept_invitation", # available to all users; access control is built into invitation logic
|
||||||
|
"applications.accept_invitation", # available to all users; access control is built into invitation logic
|
||||||
"atst.catch_all", # available to all users
|
"atst.catch_all", # available to all users
|
||||||
"portfolios.portfolios", # the portfolios list is scoped to the user separately
|
"portfolios.portfolios", # the portfolios list is scoped to the user separately
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user