diff --git a/src/country_workspace/workspaces/templates/workspace/household/change_list.html b/src/country_workspace/workspaces/templates/workspace/household/change_list.html new file mode 100644 index 0000000..d51653c --- /dev/null +++ b/src/country_workspace/workspaces/templates/workspace/household/change_list.html @@ -0,0 +1,4 @@ +{% extends "workspace/change_list.html" %} +{% block page-title %} + › Households +{% endblock %} diff --git a/tests/workspace/test_ws_backend.py b/tests/workspace/test_ws_backend.py new file mode 100644 index 0000000..f90e167 --- /dev/null +++ b/tests/workspace/test_ws_backend.py @@ -0,0 +1,110 @@ +from collections import namedtuple +from typing import TYPE_CHECKING + +from django.apps import apps +from django.contrib.auth.models import AnonymousUser + +import pytest +from testutils.perms import user_grant_permissions + +from country_workspace.state import state +from country_workspace.workspaces.backend import TenantBackend + +if TYPE_CHECKING: + from country_workspace.models import Office + +_DATA = namedtuple("_DATA", "afg,ukr") + +pytestmark = [pytest.mark.security, pytest.mark.django_db] + + +@pytest.fixture() +def data() -> _DATA: + from testutils.factories import OfficeFactory + + with state.set(must_tenant=False): + co1: "Office" = OfficeFactory(name="Afghanistan") + co2: "Office" = OfficeFactory(name="Ukraine") + return _DATA(co1, co2) + + +def pytest_generate_tests(metafunc): + if "hope_model" in metafunc.fixturenames: + models = list(apps.get_app_config("hope").get_models()) + metafunc.parametrize("hope_model", models) + + +@pytest.fixture() +def backend(): + return TenantBackend() + + +def test_aaa(backend, user): + assert not backend.has_perm(user, "aaaa") + + +def test_anonymous(backend): + assert not backend.has_perm(AnonymousUser(), "workspaces.view_countryhousehold") + + +def test_superuser(backend, data, admin_user): + with state.set(tenant=data.afg): + assert backend.has_perm(admin_user, "workspaces.view_countryhousehold") + assert backend.has_module_perms(admin_user, "workspaces.view_countryhousehold") + assert backend.get_available_modules(admin_user) == {"workspaces"} + + +def test_has_get_all_permissions_no_active_tenant(backend, data, user, admin_user): + assert backend.get_all_permissions(user) == set() + + +def test_get_all_permissions_anonymous(backend, data, user, admin_user): + with state.set(tenant=data.afg): + assert backend.get_all_permissions(AnonymousUser()) == set() + + +def test_get_all_permissions_no_enabled_tenant(backend, data, user, admin_user): + with state.set(tenant=data.afg): + assert backend.get_all_permissions(user) == set() + + +def test_get_all_permissions_no_current_tenant(backend, data, user, admin_user): + with user_grant_permissions(user, "workspaces.view_countryhousehold", country_office_or_program=data.ukr): + assert backend.get_all_permissions(user) == set() + + +def test_get_all_permissions_valid_tenant(backend, data, user, django_assert_num_queries): + with user_grant_permissions(user, "workspaces.view_countryhousehold", country_office_or_program=data.afg): + with django_assert_num_queries(1): + assert backend.get_all_permissions(user, data.afg) == {"workspaces.view_countryhousehold"} + with django_assert_num_queries(0): + # test cache + assert backend.get_all_permissions(user, data.afg) == {"workspaces.view_countryhousehold"} + + +def test_get_all_permissions_superuser(backend, data, user, admin_user): + with state.set(tenant=data.afg): + assert backend.get_all_permissions(admin_user, data.afg) + + +def test_get_allowed_tenants_user(backend, data, user, rf): + request = rf.get("/") + request.user = user + with user_grant_permissions(user, "workspaces.view_countryhousehold", country_office_or_program=data.afg): + assert backend.get_allowed_tenants(request).count() == 1 + assert backend.get_allowed_tenants(request).first() == data.afg + + +def test_get_allowed_tenants_superuser(backend, data, admin_user, rf): + request = rf.get("/") + request.user = admin_user + with state.set(tenant=data.afg, request=request): + assert backend.get_allowed_tenants().count() == 2 + assert backend.get_allowed_tenants().first() == data.afg + + +def test_get_allowed_tenants_anon(backend, data, admin_user, rf): + request = rf.get("/") + request.user = AnonymousUser() + with state.set(tenant=data.afg, request=request): + assert backend.get_allowed_tenants().count() == 0 diff --git a/tests/workspace/test_ws_security.py b/tests/workspace/test_ws_security.py new file mode 100644 index 0000000..fcd1dd5 --- /dev/null +++ b/tests/workspace/test_ws_security.py @@ -0,0 +1,138 @@ +from typing import TYPE_CHECKING + +from django.urls import reverse + +import pytest +from testutils.perms import user_grant_permissions +from testutils.utils import select_office + +from country_workspace.state import state + +if TYPE_CHECKING: + from django_webtest.pytest_plugin import MixinWithInstanceVariables + from testutils.types import CWTestApp + + from country_workspace.models import CountryHousehold, User + +pytestmark = [pytest.mark.security, pytest.mark.django_db] + + +@pytest.fixture() +def office(): + from testutils.factories import OfficeFactory + + co = OfficeFactory() + state.tenant = co + yield co + + +@pytest.fixture() +def program(office, household_checker, individual_checker): + from testutils.factories import CountryProgramFactory + + return CountryProgramFactory( + household_checker=household_checker, + individual_checker=individual_checker, + household_columns="name\nid\nxx", + individual_columns="name\nid\nxx", + ) + + +@pytest.fixture() +def household(program): + from testutils.factories import CountryHouseholdFactory + + return CountryHouseholdFactory(batch__program=program, batch__country_office=program.country_office) + + +@pytest.fixture() +def household2() -> "CountryHousehold": + from testutils.factories import BatchFactory, CountryHouseholdFactory + + b = BatchFactory() + return CountryHouseholdFactory(batch=b) + + +@pytest.fixture() +def app(django_app_factory: "MixinWithInstanceVariables", user: "User") -> "CWTestApp": + django_app = django_app_factory(csrf_checks=False) + django_app.set_user(user) + django_app._user = user + yield django_app + + +def pytest_generate_tests(metafunc: "Metafunc") -> None: # noqa + if "target" in metafunc.fixturenames: + from testutils.factories import CountryBatchFactory, CountryHouseholdFactory, CountryIndividualFactory + + targets = [ + CountryHouseholdFactory, + CountryIndividualFactory, + CountryBatchFactory, + ] + ids = ["CountryHousehold", "CountryIndividual", "CountryBatch"] + metafunc.parametrize("target", targets, ids=ids) + + +def test_user_no_permissions(db, user, target): + t = target() + assert not user.has_perm("workspaces.view_countryhousehold", t) + assert not user.has_perm("workspaces.view_countryhousehold", t.program) + assert not user.has_perm("workspaces.view_countryhousehold", t.country_office) + + +def test_grant_role_is_not_global(user, target): + t = target() + with user_grant_permissions(user, ["workspaces.view_countryhousehold"], t.program.country_office): + assert user.get_all_permissions(t.country_office) == {"workspaces.view_countryhousehold"} + assert user.get_all_permissions() == set() + + +def test_user_grant_for_office(user, target, household2: "CountryHousehold", program): + t = target() + with user_grant_permissions(user, ["workspaces.view_countryhousehold"], t.program.country_office): + assert user.has_perm("workspaces.view_countryhousehold", t.country_office) + assert user.has_perm("workspaces.view_countryhousehold", t.program) + + assert not user.has_perm("workspaces.view_countryhousehold", program) + assert not user.has_perm("workspaces.view_countryhousehold", household2.program.country_office) + assert not user.has_perm("workspaces.view_countryhousehold", household2.program) + + +def test_user_no_permissions_program(db, user, program): + assert not user.has_perm("workspaces.view_countryhousehold", program) + assert not user.has_perm("workspaces.view_countryhousehold", program.country_office) + + +def test_grant_role_is_not_global_program(user, program): + with user_grant_permissions(user, ["workspaces.view_countryhousehold"], program.country_office): + assert user.get_all_permissions(program.country_office) == {"workspaces.view_countryhousehold"} + assert user.get_all_permissions() == set() + + +def test_user_grant_for_office_program(user, household2: "CountryHousehold", program): + with user_grant_permissions(user, ["workspaces.view_countryhousehold"], program.country_office): + assert user.has_perm("workspaces.view_countryhousehold", program.country_office) + assert user.has_perm("workspaces.view_countryhousehold", program) + + assert not user.has_perm("workspaces.view_countryhousehold", household2.program.country_office) + assert not user.has_perm("workspaces.view_countryhousehold", household2.program) + + +def test_hh_office_security(app: "CWTestApp", household: "CountryHousehold", household2: "CountryHousehold") -> None: + base_url = reverse("workspace:workspaces_countryhousehold_changelist") + with user_grant_permissions(app._user, ["workspaces.view_countryhousehold"], household.country_office): + app.get("/") + with select_office(app, household.country_office): + app.get(base_url) + # with select_office(app, household.country_office, household2.program): + # app.get(base_url, status=403) + + +def test_hh_program_security(app: "CWTestApp", household: "CountryHousehold", household2: "CountryHousehold") -> None: + base_url = reverse("workspace:workspaces_countryhousehold_changelist") + with user_grant_permissions(app._user, ["workspaces.view_countryhousehold"], household.program): + with select_office(app, household.country_office): + app.get(base_url) + # with select_office(app, household.country_office, household2.program): + # app.get(base_url, status=403)