update authorization decorator to check for application permissions
This commit is contained in:
		| @@ -2,11 +2,19 @@ from atst.utils import first_or_none | |||||||
| from atst.models.permissions import Permissions | from atst.models.permissions import Permissions | ||||||
| from atst.domain.exceptions import UnauthorizedError | from atst.domain.exceptions import UnauthorizedError | ||||||
| from atst.models.portfolio_role import Status as PortfolioRoleStatus | from atst.models.portfolio_role import Status as PortfolioRoleStatus | ||||||
|  | from atst.models.application_role import Status as ApplicationRoleStatus | ||||||
|  |  | ||||||
|  |  | ||||||
| class Authorization(object): | class Authorization(object): | ||||||
|  |     @classmethod | ||||||
|  |     def has_atat_permission(cls, user, permission): | ||||||
|  |         return permission in user.permissions | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def has_portfolio_permission(cls, user, portfolio, permission): |     def has_portfolio_permission(cls, user, portfolio, permission): | ||||||
|  |         if Authorization.has_atat_permission(user, permission): | ||||||
|  |             return True | ||||||
|  |  | ||||||
|         port_role = first_or_none( |         port_role = first_or_none( | ||||||
|             lambda pr: pr.portfolio == portfolio, user.portfolio_roles |             lambda pr: pr.portfolio == portfolio, user.portfolio_roles | ||||||
|         ) |         ) | ||||||
| @@ -16,22 +24,37 @@ class Authorization(object): | |||||||
|             return False |             return False | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def has_atat_permission(cls, user, permission): |     def has_application_permission(cls, user, application, permission): | ||||||
|         return permission in user.permissions |         if Authorization.has_portfolio_permission( | ||||||
|  |             user, application.portfolio, permission | ||||||
|  |         ): | ||||||
|  |             return True | ||||||
|  |  | ||||||
|  |         app_role = first_or_none( | ||||||
|  |             lambda app_role: app_role.application == application, user.application_roles | ||||||
|  |         ) | ||||||
|  |         if app_role and app_role.status is not ApplicationRoleStatus.DISABLED: | ||||||
|  |             return permission in app_role.permissions | ||||||
|  |         else: | ||||||
|  |             return False | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def check_portfolio_permission(cls, user, portfolio, permission, message): |     def check_atat_permission(cls, user, permission, message): | ||||||
|         if not ( |         if not Authorization.has_atat_permission(user, permission): | ||||||
|             Authorization.has_atat_permission(user, permission) |  | ||||||
|             or Authorization.has_portfolio_permission(user, portfolio, permission) |  | ||||||
|         ): |  | ||||||
|             raise UnauthorizedError(user, message) |             raise UnauthorizedError(user, message) | ||||||
|  |  | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def check_atat_permission(cls, user, permission, message): |     def check_portfolio_permission(cls, user, portfolio, permission, message): | ||||||
|         if not Authorization.has_atat_permission(user, permission): |         if not Authorization.has_portfolio_permission(user, portfolio, permission): | ||||||
|  |             raise UnauthorizedError(user, message) | ||||||
|  |  | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def check_application_permission(cls, user, portfolio, permission, message): | ||||||
|  |         if not Authorization.has_application_permission(user, portfolio, permission): | ||||||
|             raise UnauthorizedError(user, message) |             raise UnauthorizedError(user, message) | ||||||
|  |  | ||||||
|         return True |         return True | ||||||
| @@ -70,8 +93,12 @@ class Authorization(object): | |||||||
|             raise UnauthorizedError(user, message) |             raise UnauthorizedError(user, message) | ||||||
|  |  | ||||||
|  |  | ||||||
| def user_can_access(user, permission, portfolio=None, message=None): | def user_can_access(user, permission, portfolio=None, application=None, message=None): | ||||||
|     if portfolio: |     if application: | ||||||
|  |         Authorization.check_application_permission( | ||||||
|  |             user, application, permission, message | ||||||
|  |         ) | ||||||
|  |     elif portfolio: | ||||||
|         Authorization.check_portfolio_permission(user, portfolio, permission, message) |         Authorization.check_portfolio_permission(user, portfolio, permission, message) | ||||||
|     else: |     else: | ||||||
|         Authorization.check_atat_permission(user, permission, message) |         Authorization.check_atat_permission(user, permission, message) | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ def check_access(permission, message, override, *args, **kwargs): | |||||||
|  |  | ||||||
|     if "application_id" in kwargs: |     if "application_id" in kwargs: | ||||||
|         application = Applications.get(kwargs["application_id"]) |         application = Applications.get(kwargs["application_id"]) | ||||||
|  |         access_args["application"] = application | ||||||
|         access_args["portfolio"] = application.portfolio |         access_args["portfolio"] = application.portfolio | ||||||
|  |  | ||||||
|     elif "task_order_id" in kwargs: |     elif "task_order_id" in kwargs: | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| from tests.factories import ( | from tests.factories import ( | ||||||
|  |     ApplicationRoleFactory, | ||||||
|     TaskOrderFactory, |     TaskOrderFactory, | ||||||
|     UserFactory, |     UserFactory, | ||||||
|     PortfolioFactory, |     PortfolioFactory, | ||||||
| @@ -69,6 +70,22 @@ def test_has_portfolio_permission(): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_has_application_permission(): | ||||||
|  |     role_one = PermissionSets.get(PermissionSets.EDIT_APPLICATION_TEAM) | ||||||
|  |     role_two = PermissionSets.get(PermissionSets.EDIT_APPLICATION_ENVIRONMENTS) | ||||||
|  |     app_role = ApplicationRoleFactory.create(permission_sets=[role_one, role_two]) | ||||||
|  |     different_user = UserFactory.create() | ||||||
|  |     assert Authorization.has_application_permission( | ||||||
|  |         app_role.user, app_role.application, Permissions.EDIT_ENVIRONMENT | ||||||
|  |     ) | ||||||
|  |     assert not Authorization.has_portfolio_permission( | ||||||
|  |         app_role.user, app_role.application, Permissions.DELETE_ENVIRONMENT | ||||||
|  |     ) | ||||||
|  |     assert not Authorization.has_portfolio_permission( | ||||||
|  |         different_user, app_role.application, Permissions.DELETE_ENVIRONMENT | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_user_can_access(): | def test_user_can_access(): | ||||||
|     ccpo = UserFactory.create_ccpo() |     ccpo = UserFactory.create_ccpo() | ||||||
|     edit_admin = UserFactory.create() |     edit_admin = UserFactory.create() | ||||||
| @@ -120,7 +137,23 @@ def set_current_user(request_ctx): | |||||||
|     request_ctx.g.current_user = None |     request_ctx.g.current_user = None | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_user_can_access_decorator(set_current_user): | def test_user_can_access_decorator_atat_level(set_current_user): | ||||||
|  |     ccpo = UserFactory.create_ccpo() | ||||||
|  |     rando = UserFactory.create() | ||||||
|  |  | ||||||
|  |     @user_can_access_decorator(Permissions.VIEW_AUDIT_LOG) | ||||||
|  |     def _access_activity_log(*args, **kwargs): | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     set_current_user(ccpo) | ||||||
|  |     assert _access_activity_log() | ||||||
|  |  | ||||||
|  |     set_current_user(rando) | ||||||
|  |     with pytest.raises(UnauthorizedError): | ||||||
|  |         _access_activity_log() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_user_can_access_decorator_portfolio_level(set_current_user): | ||||||
|     ccpo = UserFactory.create_ccpo() |     ccpo = UserFactory.create_ccpo() | ||||||
|     edit_admin = UserFactory.create() |     edit_admin = UserFactory.create() | ||||||
|     view_admin = UserFactory.create() |     view_admin = UserFactory.create() | ||||||
| @@ -144,6 +177,36 @@ def test_user_can_access_decorator(set_current_user): | |||||||
|         _edit_portfolio_name(portfolio_id=portfolio.id) |         _edit_portfolio_name(portfolio_id=portfolio.id) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_user_can_access_decorator_application_level(set_current_user): | ||||||
|  |     ccpo = UserFactory.create_ccpo() | ||||||
|  |     port_admin = UserFactory.create() | ||||||
|  |     app_user = UserFactory.create() | ||||||
|  |     rando = UserFactory.create() | ||||||
|  |  | ||||||
|  |     portfolio = PortfolioFactory.create( | ||||||
|  |         owner=port_admin, applications=[{"name": "Mos Eisley"}] | ||||||
|  |     ) | ||||||
|  |     app = portfolio.applications[0] | ||||||
|  |     ApplicationRoleFactory.create(application=app, user=app_user) | ||||||
|  |  | ||||||
|  |     @user_can_access_decorator(Permissions.VIEW_APPLICATION) | ||||||
|  |     def _stroll_into_mos_eisley(*args, **kwargs): | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     set_current_user(ccpo) | ||||||
|  |     assert _stroll_into_mos_eisley(application_id=app.id) | ||||||
|  |  | ||||||
|  |     set_current_user(port_admin) | ||||||
|  |     assert _stroll_into_mos_eisley(application_id=app.id) | ||||||
|  |  | ||||||
|  |     set_current_user(app_user) | ||||||
|  |     assert _stroll_into_mos_eisley(application_id=app.id) | ||||||
|  |  | ||||||
|  |     set_current_user(rando) | ||||||
|  |     with pytest.raises(UnauthorizedError): | ||||||
|  |         _stroll_into_mos_eisley(application_id=app.id) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_user_can_access_decorator_override(set_current_user): | def test_user_can_access_decorator_override(set_current_user): | ||||||
|     rando_calrissian = UserFactory.create() |     rando_calrissian = UserFactory.create() | ||||||
|     darth_vader = UserFactory.create() |     darth_vader = UserFactory.create() | ||||||
|   | |||||||
| @@ -63,6 +63,10 @@ def base_portfolio_permission_sets(): | |||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def base_application_permission_sets(): | ||||||
|  |     return [PermissionSets.get(PermissionSets.VIEW_APPLICATION)] | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_all_portfolio_permission_sets(): | def get_all_portfolio_permission_sets(): | ||||||
|     return PermissionSets.get_many(PortfolioRoles.PORTFOLIO_PERMISSION_SETS) |     return PermissionSets.get_many(PortfolioRoles.PORTFOLIO_PERMISSION_SETS) | ||||||
|  |  | ||||||
| @@ -220,7 +224,7 @@ class ApplicationRoleFactory(Base): | |||||||
|     application = factory.SubFactory(ApplicationFactory) |     application = factory.SubFactory(ApplicationFactory) | ||||||
|     user = factory.SubFactory(UserFactory) |     user = factory.SubFactory(UserFactory) | ||||||
|     status = ApplicationRoleStatus.PENDING |     status = ApplicationRoleStatus.PENDING | ||||||
|     permission_sets = [] |     permission_sets = factory.LazyFunction(base_application_permission_sets) | ||||||
|  |  | ||||||
|  |  | ||||||
| class EnvironmentRoleFactory(Base): | class EnvironmentRoleFactory(Base): | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ from atst.models.portfolio_role import Status as PortfolioRoleStatus | |||||||
|  |  | ||||||
| from tests.factories import ( | from tests.factories import ( | ||||||
|     AttachmentFactory, |     AttachmentFactory, | ||||||
|  |     ApplicationRoleFactory, | ||||||
|     InvitationFactory, |     InvitationFactory, | ||||||
|     PortfolioFactory, |     PortfolioFactory, | ||||||
|     PortfolioRoleFactory, |     PortfolioRoleFactory, | ||||||
| @@ -156,12 +157,14 @@ def test_portfolios_access_environment_access(get_url_assert_status): | |||||||
| def test_portfolios_application_members_access(get_url_assert_status): | def test_portfolios_application_members_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_dev = user_with() | ||||||
|     rando = user_with() |     rando = user_with() | ||||||
|     portfolio = PortfolioFactory.create( |     portfolio = PortfolioFactory.create( | ||||||
|         owner=owner, |         owner=owner, | ||||||
|         applications=[{"name": "Mos Eisley", "description": "Where Han shot first"}], |         applications=[{"name": "Mos Eisley", "description": "Where Han shot first"}], | ||||||
|     ) |     ) | ||||||
|     app = portfolio.applications[0] |     app = portfolio.applications[0] | ||||||
|  |     ApplicationRoleFactory.create(application=app, user=app_dev) | ||||||
|  |  | ||||||
|     url = url_for( |     url = url_for( | ||||||
|         "portfolios.application_members", |         "portfolios.application_members", | ||||||
| @@ -170,6 +173,7 @@ def test_portfolios_application_members_access(get_url_assert_status): | |||||||
|     ) |     ) | ||||||
|     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_dev, url, 200) | ||||||
|     get_url_assert_status(rando, url, 404) |     get_url_assert_status(rando, url, 404) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -570,7 +574,7 @@ def test_portfolios_update_member_access(post_url_assert_status): | |||||||
|     prt_member = user_with() |     prt_member = user_with() | ||||||
|  |  | ||||||
|     portfolio = PortfolioFactory.create(owner=owner) |     portfolio = PortfolioFactory.create(owner=owner) | ||||||
|     prr = PortfolioRoleFactory.create(user=prt_member, portfolio=portfolio) |     PortfolioRoleFactory.create(user=prt_member, portfolio=portfolio) | ||||||
|  |  | ||||||
|     url = url_for( |     url = url_for( | ||||||
|         "portfolios.update_member", portfolio_id=portfolio.id, member_id=prt_member.id |         "portfolios.update_member", portfolio_id=portfolio.id, member_id=prt_member.id | ||||||
| @@ -588,7 +592,7 @@ def test_portfolios_view_member_access(get_url_assert_status): | |||||||
|     prt_member = user_with() |     prt_member = user_with() | ||||||
|  |  | ||||||
|     portfolio = PortfolioFactory.create(owner=owner) |     portfolio = PortfolioFactory.create(owner=owner) | ||||||
|     prr = PortfolioRoleFactory.create(user=prt_member, portfolio=portfolio) |     PortfolioRoleFactory.create(user=prt_member, portfolio=portfolio) | ||||||
|  |  | ||||||
|     url = url_for( |     url = url_for( | ||||||
|         "portfolios.view_member", portfolio_id=portfolio.id, member_id=prt_member.id |         "portfolios.view_member", portfolio_id=portfolio.id, member_id=prt_member.id | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user