diff --git a/caseworker/core/rules.py b/caseworker/core/rules.py index b732cfbc9f..6cbc88a6bc 100644 --- a/caseworker/core/rules.py +++ b/caseworker/core/rules.py @@ -12,6 +12,7 @@ ) from caseworker.core.constants import ( ADMIN_TEAM_ID, + SUPER_USER_ROLE_ID, TAU_TEAM_ID, LICENSING_UNIT_TEAM_ID, LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, @@ -180,6 +181,21 @@ def is_organisation_active(request, organisation): return organisation["status"]["key"] == "active" +@rules.predicate +def is_super_user(request): + user = get_logged_in_caseworker(request) + return user.get("role", {}).get("id") == SUPER_USER_ROLE_ID + + +@rules.predicate +def check_user_is_not_logged_in_caseworker(request, user): + caseworker = get_logged_in_caseworker(request) + caseworker_user_id = caseworker["id"] + if not user: + return True + return user.get("user", {}).get("id") != caseworker_user_id + + rules.add_rule("can_user_allocate_case", is_case_caseworker_operable) rules.add_rule("can_user_change_case", is_user_allocated) rules.add_rule("can_user_move_case_forward", is_user_allocated) @@ -205,3 +221,9 @@ def is_organisation_active(request, organisation): "can_licence_status_be_changed", is_user_licencing_unit_senior_manager & is_case_finalised_and_licence_editable ) rules.add_rule("can_user_manage_organisation", is_user_manage_organisations_role & is_organisation_active) +rules.add_rule("can_caseworker_edit_role", is_super_user | is_user_in_admin_team) # noqa +rules.add_rule("can_caseworker_edit_team", is_super_user | is_user_in_admin_team) # noqa +rules.add_rule( + "can_caseworker_deactivate", + (is_super_user | is_user_in_admin_team) & check_user_is_not_logged_in_caseworker, # noqa +) diff --git a/caseworker/templates/users/profile.html b/caseworker/templates/users/profile.html index 7c0b23abe1..7ea285ab67 100644 --- a/caseworker/templates/users/profile.html +++ b/caseworker/templates/users/profile.html @@ -1,4 +1,5 @@ {% extends 'layouts/two-pane.html' %} +{% load rules %} {% block back_link %} @@ -20,8 +21,8 @@

- {% if can_deactivate %} - {% if request.session.lite_api_user_id|stringformat:"s" != data.user.id|stringformat:"s" %} + {% test_rule 'can_caseworker_deactivate' request data as show_deactivate %} + {% if show_deactivate %} {% if data.user.status == 'Active' %} {% lcs 'users.UserProfile.DEACTIVATE_BUTTON' %} @@ -31,7 +32,6 @@

{% lcs 'users.UserProfile.REACTIVATE_BUTTON' %} {% endif %} - {% endif %} {% endif %}

@@ -81,7 +81,8 @@

{{ data.user.team.name }}
- {% if can_edit_team %} + {% test_rule 'can_caseworker_edit_team' request as show_edit_team %} + {% if show_edit_team %} {% lcs 'users.UserProfile.SummaryList.CHANGE' %} {% lcs 'users.UserProfile.SummaryList.TEAM' %} @@ -97,7 +98,8 @@

{{ data.user.role.name }}

- {% if can_edit_role %} + {% test_rule 'can_caseworker_edit_role' request as show_edit_role %} + {% if show_edit_role %} {% lcs 'users.UserProfile.SummaryList.CHANGE' %} {% lcs 'users.UserProfile.SummaryList.ROLE' %} diff --git a/caseworker/users/views/users.py b/caseworker/users/views/users.py index c67337d620..3f2e1621c5 100644 --- a/caseworker/users/views/users.py +++ b/caseworker/users/views/users.py @@ -9,7 +9,6 @@ from core.auth.views import LoginRequiredMixin from caseworker.core.constants import ( ADMIN_TEAM_ID, - SUPER_USER_ROLE_ID, UserStatuses, ) from lite_content.lite_internal_frontend import strings @@ -78,19 +77,8 @@ def get_success_url(self): class ViewUser(TemplateView): def get(self, request, **kwargs): data, _ = get_gov_user(request, str(kwargs["pk"])) - request_user, _ = get_gov_user(request, str(request.session["lite_api_user_id"])) - super_user = is_super_user(request_user) - can_deactivate = not is_super_user(data) - can_edit_role = data["user"]["id"] != request.session["lite_api_user_id"] - can_edit_team = super_user or is_user_in_team(request_user, ADMIN_TEAM_ID) - context = { "data": data, - "super_user": super_user, - "super_user_role_id": SUPER_USER_ROLE_ID, - "can_deactivate": can_deactivate, - "can_edit_role": can_edit_role, - "can_edit_team": can_edit_team, } return render(request, "users/profile.html", context) diff --git a/exporter/applications/views/security_approvals/forms.py b/exporter/applications/views/security_approvals/forms.py index 3b6d5544c6..ab3d921185 100644 --- a/exporter/applications/views/security_approvals/forms.py +++ b/exporter/applications/views/security_approvals/forms.py @@ -76,7 +76,7 @@ class Layout: TITLE = "Are any products on this application subject to ITAR controls?" label = """ - We need to know if this export involves any defence articles including technical data that are + We need to know if this export involves any defence articles including technical data that are subject to controls under the United States (US) International Traffic in Arms regulations (ITAR). """ @@ -126,11 +126,9 @@ class Layout: ) f1686_reference_number = forms.CharField( + required=False, widget=forms.TextInput, - label="Reference number", - error_messages={ - "required": "Enter a reference number", - }, + label="Reference number (optional)", ) f1686_approval_date = CustomErrorDateInputField( diff --git a/unit_tests/caseworker/core/test_rules.py b/unit_tests/caseworker/core/test_rules.py index eae72daedd..80c0324783 100644 --- a/unit_tests/caseworker/core/test_rules.py +++ b/unit_tests/caseworker/core/test_rules.py @@ -19,6 +19,7 @@ ADMIN_TEAM_ID, FCDO_TEAM_ID, LICENSING_UNIT_TEAM_ID, + SUPER_USER_ROLE_ID, TAU_TEAM_ID, LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, Permission, @@ -698,3 +699,74 @@ def test_can_user_manage_organisation( request = get_mock_request(user) data_organisation["status"]["key"] = organisation_status assert rules.test_rule("can_user_manage_organisation", request, data_organisation) is expected + + +@pytest.mark.parametrize( + ("user_role", "user_team", "expected"), + ( + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, True), + (SUPER_USER_ROLE_ID, TAU_TEAM_ID, True), + (LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, ADMIN_TEAM_ID, True), + (LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, TAU_TEAM_ID, False), + (None, None, False), + ), +) +def test_can_caseworker_edit_role(mock_gov_user, get_mock_request, user_team, user_role, expected): + user = mock_gov_user["user"] + user["role"]["id"] = user_role + user["team"]["id"] = user_team + request = get_mock_request(user) + assert rules.test_rule("can_caseworker_edit_role", request) is expected + + +@pytest.mark.parametrize( + ("user_role", "user_team", "expected"), + ( + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, True), + (SUPER_USER_ROLE_ID, TAU_TEAM_ID, True), + (LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, ADMIN_TEAM_ID, True), + (LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, TAU_TEAM_ID, False), + (None, None, False), + ), +) +def test_can_caseworker_edit_team(mock_gov_user, get_mock_request, user_team, user_role, expected): + + user = mock_gov_user["user"] + user["role"]["id"] = user_role + user["team"]["id"] = user_team + + request = get_mock_request(user) + assert rules.test_rule("can_caseworker_edit_team", request) is expected + + +@pytest.mark.parametrize( + ("user_role", "user_team", "user_data", "expected"), + ( + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, {"user": {"id": "123"}}, True), + (SUPER_USER_ROLE_ID, TAU_TEAM_ID, {"user": {"id": "123"}}, True), + (LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, ADMIN_TEAM_ID, {"user": {"id": "123"}}, True), + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, {"user": {"id": "2a43805b-c082-47e7-9188-c8b3e1a83cb0"}}, False), + (SUPER_USER_ROLE_ID, TAU_TEAM_ID, {"user": {"id": "2a43805b-c082-47e7-9188-c8b3e1a83cb0"}}, False), + ( + LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, + ADMIN_TEAM_ID, + {"user": {"id": "2a43805b-c082-47e7-9188-c8b3e1a83cb0"}}, + False, + ), + ( + LICENSING_UNIT_SENIOR_MANAGER_ROLE_ID, + TAU_TEAM_ID, + {"user": {"id": "2a43805b-c082-47e7-9188-c8b3e1a83cb0"}}, + False, + ), + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, None, True), + (None, None, None, False), + ), +) +def test_can_caseworker_deactivate(mock_gov_user, get_mock_request, user_team, user_role, user_data, expected): + + user = mock_gov_user["user"] + user["role"]["id"] = user_role + user["team"]["id"] = user_team + request = get_mock_request(user) + assert rules.test_rule("can_caseworker_deactivate", request, user_data) is expected diff --git a/unit_tests/caseworker/users/test_views.py b/unit_tests/caseworker/users/test_views.py index 187827cc44..4e06494472 100644 --- a/unit_tests/caseworker/users/test_views.py +++ b/unit_tests/caseworker/users/test_views.py @@ -200,6 +200,121 @@ def test_view_user_edit_team_permissions( assert (soup.find("a", {"id": "link-edit-team"}) is not None) == can_edit_team +@pytest.mark.parametrize( + "role_id, team_id, can_edit_role", + ( + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, True), + (NON_SUPER_USER_ROLE_ID, NON_ADMIN_TEAM_ID, False), + (SUPER_USER_ROLE_ID, NON_ADMIN_TEAM_ID, True), + (NON_SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, True), + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, True), + ), +) +def test_view_user_edit_role_permissions( + authorized_client, + requests_mock, + mock_gov_user, + role_id, + team_id, + can_edit_role, +): + user_id = str(uuid.uuid4()) + + mock_gov_user["user"] = { + "id": user_id, + "role": { + "id": role_id, + "permissions": {}, + "name": "Test Role", + }, + "team": { + "id": team_id, + }, + } + requests_mock.get( + client._build_absolute_uri(f"/gov-users/{user_id}/"), + json={ + "user": { + "id": user_id, + "role": { + "id": role_id, + }, + "team": { + "id": team_id, + }, + }, + }, + ) + + url = reverse("users:user", kwargs={"pk": user_id}) + response = authorized_client.get(url) + + soup = BeautifulSoup(response.content, "html.parser") + assert (soup.find("a", {"id": "link-edit-role"}) is not None) == can_edit_role + + +@pytest.mark.parametrize( + "role_id, team_id, edit_user_id ,can_deactivate", + ( + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, "2a43805b-c082-47e7-9188-c8b3e1a83cb0", False), + (SUPER_USER_ROLE_ID, NON_ADMIN_TEAM_ID, "2a43805b-c082-47e7-9188-c8b3e1a83cb0", False), + (NON_SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, "2a43805b-c082-47e7-9188-c8b3e1a83cb0", False), + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, "4a43805b-c082-47e7-9188-c8b3e1a83cb1", True), + (NON_SUPER_USER_ROLE_ID, NON_ADMIN_TEAM_ID, "4a43805b-c082-47e7-9188-c8b3e1a83cb1", False), ##£`` + (SUPER_USER_ROLE_ID, NON_ADMIN_TEAM_ID, "4a43805b-c082-47e7-9188-c8b3e1a83cb1", True), + (NON_SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, "4a43805b-c082-47e7-9188-c8b3e1a83cb1", True), + (SUPER_USER_ROLE_ID, ADMIN_TEAM_ID, "4a43805b-c082-47e7-9188-c8b3e1a83cb1", True), + ), +) +def test_view_user_deactivate_permissions( + authorized_client, + requests_mock, + mock_gov_user, + role_id, + team_id, + edit_user_id, + can_deactivate, +): + + gov_user_id = mock_gov_user["user"]["id"] + + requests_mock.get( + client._build_absolute_uri(f"/gov-users/{gov_user_id}/"), + json={ + "user": { + "id": gov_user_id, + "role": { + "id": role_id, + }, + "team": { + "id": team_id, + }, + }, + }, + ) + requests_mock.get( + client._build_absolute_uri(f"/gov-users/{edit_user_id}/"), + json={ + "user": { + "id": edit_user_id, + "role": { + "id": role_id, + }, + "team": { + "id": team_id, + }, + "status": "Active", + }, + }, + ) + + url = reverse("users:user", kwargs={"pk": edit_user_id}) + response = authorized_client.get(url) + + soup = BeautifulSoup(response.content, "html.parser") + assert (soup.find("a", {"id": "button-deactivate-user"}) is not None) == can_deactivate + + @pytest.mark.parametrize( "role_id, team_id, can_edit_team, team_payload", ( diff --git a/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_edit_views.py b/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_edit_views.py index ebcb1b288b..2878d7e6cc 100644 --- a/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_edit_views.py +++ b/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_edit_views.py @@ -83,6 +83,20 @@ def post_to_edit_security_approvals(post_to_step_factory, edit_security_approval "f1686_approval_date": "2020-02-02", }, ), + ( + "edit_security_approvals_f1686_details", + { + "f1686_contracting_authority": "some text", + "f1686_approval_date_0": "01", + "f1686_approval_date_1": "01", + "f1686_approval_date_2": "2022", + }, + { + "f1686_contracting_authority": "some text", + "f1686_reference_number": "", + "f1686_approval_date": "2022-01-01", + }, + ), ), ) def test_edit_export_details_post( diff --git a/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_forms.py b/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_forms.py index 6e3820eabb..029f732518 100644 --- a/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_forms.py +++ b/unit_tests/exporter/applications/views/security_approvals/test_security_approvals_forms.py @@ -118,7 +118,6 @@ def test_security_other_details(data, is_valid, errors): { "f1686_contracting_authority": ["Enter the contracting authority (or signatory and job role)"], "f1686_approval_date": ["Enter the approval date"], - "f1686_reference_number": ["Enter a reference number"], }, ), (