From 2a3e22cb7d3459b0ca640bdbff72ddaf0111622c Mon Sep 17 00:00:00 2001 From: HAKIZIMANA Franck Date: Wed, 14 Aug 2024 18:59:33 +0200 Subject: [PATCH 1/6] add orgUnit read permission --- .../Iaso/domains/app/translations/en.json | 2 + .../Iaso/domains/app/translations/fr.json | 2 + .../Iaso/domains/users/permissionsMessages.ts | 8 ++ hat/assets/js/apps/Iaso/utils/permissions.ts | 2 + hat/menupermissions/constants.py | 2 + ...5_alter_custompermissionsupport_options.py | 136 ++++++++++++++++++ hat/menupermissions/models.py | 3 + iaso/api/org_units.py | 20 ++- 8 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 hat/menupermissions/migrations/0065_alter_custompermissionsupport_options.py diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/en.json b/hat/assets/js/apps/Iaso/domains/app/translations/en.json index a53d7bb7e8..4d8dd415bd 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/en.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/en.json @@ -961,6 +961,7 @@ "iaso.permissions.mappings": "DHIS2 mappings", "iaso.permissions.modules": "Modules", "iaso.permissions.orgUnits": "Organisation units management", + "iaso.permissions.orgUnits_read": "Organisation units - Read", "iaso.permissions.pages": "Web embedded links management - Read only", "iaso.permissions.planning": "Planning", "iaso.permissions.planning_read": "Planning - Read", @@ -998,6 +999,7 @@ "iaso.permissions.tooltip.mappings": "Match DHIS2 and IASO data elements for data exchanges", "iaso.permissions.tooltip.modules": "View modules linked to the current account", "iaso.permissions.tooltip.orgUnits": "Manage organisation units and pyramids, including uploading of geo data (GPS coordinates and shapes), and groups", + "iaso.permissions.tooltip.orgUnits_read": "See and read organisation units and pyramids", "iaso.permissions.tooltip.pages": "List of external links (dashboards for instance)", "iaso.permissions.tooltip.planning_read": "See and read planning", "iaso.permissions.tooltip.planning_write": "Edit and add planning", diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json index ab3f874908..0c7d0e9872 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json @@ -961,6 +961,7 @@ "iaso.permissions.mappings": "Liens avec DHIS2", "iaso.permissions.modules": "Modules", "iaso.permissions.orgUnits": "Gestion des unités d’organisation", + "iaso.permissions.orgUnits_read": "Unités d'organisation - Lecture", "iaso.permissions.pages": "Gestion des liens intégrés web - lecture seule", "iaso.permissions.planning": "Planning", "iaso.permissions.planning_read": "Planification - Lire", @@ -998,6 +999,7 @@ "iaso.permissions.tooltip.mappings": "Edition des liens entre les éléments de données DHIS2 et IASO pour les échanges de données", "iaso.permissions.tooltip.modules": "Voir les modules liés au compte courant", "iaso.permissions.tooltip.orgUnits": "Gestion des unités d’organisation et de la pyramide, y compris le chargement de données géographiques (points GPS et contours) et la gestion des groupes", + "iaso.permissions.tooltip.orgUnits_read": "Voir et lire les unités d'organisation et les pyramides", "iaso.permissions.tooltip.pages": "Liste des liens externes (tableaux de bords par exemple)", "iaso.permissions.tooltip.planning_read": "Voir et lire le planning", "iaso.permissions.tooltip.planning_write": "Modifier et ajouter une planification", diff --git a/hat/assets/js/apps/Iaso/domains/users/permissionsMessages.ts b/hat/assets/js/apps/Iaso/domains/users/permissionsMessages.ts index ad9de2337b..e8ac73d015 100644 --- a/hat/assets/js/apps/Iaso/domains/users/permissionsMessages.ts +++ b/hat/assets/js/apps/Iaso/domains/users/permissionsMessages.ts @@ -127,6 +127,14 @@ const PERMISSIONS_MESSAGES = defineMessages({ defaultMessage: 'Manage organisation units and pyramids, including uploading of geo data (GPS coordinates and shapes), and groups', }, + iaso_org_units_read: { + id: 'iaso.permissions.orgUnits_read', + defaultMessage: 'Organisation units - Read', + }, + iaso_org_units_read_tooltip: { + id: 'iaso.permissions.tooltip.orgUnits_read', + defaultMessage: 'See and read organisation units and pyramids', + }, iaso_submissions: { id: 'iaso.permissions.submissions', defaultMessage: 'Submissions - Read only', diff --git a/hat/assets/js/apps/Iaso/utils/permissions.ts b/hat/assets/js/apps/Iaso/utils/permissions.ts index ed2451cc48..08fbfd7c89 100644 --- a/hat/assets/js/apps/Iaso/utils/permissions.ts +++ b/hat/assets/js/apps/Iaso/utils/permissions.ts @@ -13,6 +13,7 @@ const MAPPINGS = 'iaso_mappings'; const ORG_UNIT_GROUPS = 'iaso_org_unit_groups'; const ORG_UNIT_TYPES = 'iaso_org_unit_types'; const ORG_UNITS = 'iaso_org_units'; +const ORG_UNITS_READ = 'iaso_org_units_read'; const PAGES = 'iaso_pages'; const PAGE_WRITE = 'iaso_page_write'; const PLANNING_READ = 'iaso_planning_read'; @@ -59,6 +60,7 @@ export { MAPPINGS, MODULES, ORG_UNITS, + ORG_UNITS_READ, ORG_UNITS_CHANGE_REQUEST_REVIEW, ORG_UNIT_GROUPS, ORG_UNIT_TYPES, diff --git a/hat/menupermissions/constants.py b/hat/menupermissions/constants.py index a277bf0af4..debc1dd257 100644 --- a/hat/menupermissions/constants.py +++ b/hat/menupermissions/constants.py @@ -8,6 +8,7 @@ ], "DEFAULT": [ "iaso_org_units", + "iaso_org_units_read", "iaso_org_unit_types", "iaso_org_unit_groups", "iaso_sources", @@ -94,6 +95,7 @@ ], "org_units": [ "iaso_org_units", + "iaso_org_units_read", "iaso_org_unit_types", "iaso_org_unit_groups", "iaso_sources", diff --git a/hat/menupermissions/migrations/0065_alter_custompermissionsupport_options.py b/hat/menupermissions/migrations/0065_alter_custompermissionsupport_options.py new file mode 100644 index 0000000000..011b21c3a6 --- /dev/null +++ b/hat/menupermissions/migrations/0065_alter_custompermissionsupport_options.py @@ -0,0 +1,136 @@ +# Generated by Django 4.2.14 on 2024-08-13 12:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("menupermissions", "0064_alter_custompermissionsupport_options"), + ] + + operations = [ + migrations.AlterModelOptions( + name="custompermissionsupport", + options={ + "managed": False, + "permissions": ( + ("iaso_forms", "Formulaires"), + ("iaso_forms_stats", "Statistiques pour les formulaires"), + ("iaso_mappings", "Correspondances avec DHIS2"), + ("iaso_modules", "modules"), + ("iaso_completeness", "Complétude des données"), + ("iaso_org_units", "Unités d'organisations"), + ("iaso_org_units_read", "Lire les unités d'organisations"), + ("iaso_registry_write", "Editer le Registre"), + ("iaso_registry_read", "Lire le Registre"), + ("iaso_links", "Correspondances sources"), + ("iaso_users", "Users"), + ("iaso_users_managed", "Users managed"), + ("iaso_pages", "Pages"), + ("iaso_projects", "Projets"), + ("iaso_sources", "Sources"), + ("iaso_data_tasks", "Tâches"), + ("iaso_submissions", "Soumissions"), + ("iaso_update_submission", "Editer soumissions"), + ("iaso_planning_write", "Editer le planning"), + ("iaso_planning_read", "Lire le planning"), + ("iaso_reports", "Reports"), + ("iaso_teams", "Equipes"), + ("iaso_assignments", "Attributions"), + ("iaso_polio_budget", "Budget Polio"), + ("iaso_entities", "Entities"), + ("iaso_entity_type_write", "Write entity type"), + ("iaso_storages", "Storages"), + ("iaso_completeness_stats", "Completeness stats"), + ("iaso_workflows", "Workflows"), + ("iaso_polio_budget_admin", "Budget Polio Admin"), + ("iaso_entity_duplicates_read", "Read Entity duplicates"), + ("iaso_entity_duplicates_write", "Write Entity duplicates"), + ("iaso_user_roles", "Manage user roles"), + ("iaso_datastore_read", "Read data store"), + ("iaso_datastore_write", "Write data store"), + ("iaso_org_unit_types", "Org unit types"), + ("iaso_org_unit_groups", "Org unit groups"), + ( + "iaso_org_unit_change_request_review", + "Org unit change request review", + ), + ("iaso_write_sources", "Write data source"), + ("iaso_page_write", "Write page"), + ("iaso_payments", "Payments page"), + ("iaso_polio", "Polio"), + ("iaso_polio_config", "Polio config"), + ("iaso_polio_chronogram", "Polio chronogram"), + ( + "iaso_polio_chronogram_restricted_write", + "Polio chronogram user (restricted write)", + ), + ("iaso_polio_notifications", "Polio notifications"), + ( + "iaso_polio_vaccine_authorizations_read_only", + "Polio Vaccine Authorizations Read Only", + ), + ( + "iaso_polio_vaccine_authorizations_admin", + "Polio Vaccine Authorizations Admin", + ), + ( + "iaso_polio_vaccine_supply_chain_read", + "Polio Vaccine Supply Chain Read", + ), + ( + "iaso_polio_vaccine_supply_chain_write", + "Polio Vaccine Supply Chain Write", + ), + ( + "iaso_polio_vaccine_stock_management_read", + "Polio Vaccine Stock Management Read", + ), + ( + "iaso_polio_vaccine_stock_management_write", + "Polio Vaccine Stock Management Write", + ), + ("iaso_trypelim_anonymous", "Anonymisation des patients"), + ("iaso_trypelim_management_areas", "Areas"), + ("iaso_trypelim_management_edit_areas", "Edit areas"), + ("iaso_trypelim_management_edit_shape_areas", "Edit areas shapes"), + ("iaso_trypelim_case_cases", "Cases"), + ("iaso_trypelim_case_analysis", "Cases analysis"), + ("iaso_trypelim_management_coordinations", "Coordinations"), + ("iaso_trypelim_management_devices", "Devices"), + ("iaso_trypelim_datas_download", "Téléchargement de données"), + ("iaso_trypelim_duplicates", "Doublons"), + ("iaso_trypelim_datas_patient_edition", "Edition d'un patient"), + ("iaso_trypelim_stats_graphs", "Graphs"), + ("iaso_trypelim_management_health_structures", "Health facilities"), + ("iaso_trypelim_lab", "Labo"), + ("iaso_trypelim_labupload", "Labo import"), + ("iaso_trypelim_locator", "Locator"), + ("iaso_trypelim_plannings_macroplanning", "Macroplanning"), + ("iaso_trypelim_plannings_microplanning", "Microplanning"), + ("iaso_trypelim_modifications", "Modifications"), + ("iaso_trypelim_management_plannings", "Plannings"), + ( + "iaso_trypelim_management_plannings_template", + "Plannings template", + ), + ("iaso_trypelim_qualitycontrol", "Quality control"), + ("iaso_trypelim_case_reconciliation", "Reconciliation"), + ("iaso_trypelim_plannings_routes", "Routes"), + ("iaso_trypelim_datasets_datauploads", "Upload of cases files"), + ( + "iaso_trypelim_datasets_villageuploads", + "Upload of villages files", + ), + ("iaso_trypelim_management_users", "Users"), + ("iaso_trypelim_vectorcontrol", "Vector control"), + ("iaso_trypelim_vectorcontrolupload", "Vector control import Gpx"), + ("iaso_trypelim_management_villages", "Villages"), + ("iaso_trypelim_management_workzones", "Work zones"), + ("iaso_trypelim_management_zones", "Zones"), + ("iaso_trypelim_management_edit_zones", "Edit zones"), + ("iaso_trypelim_management_edit_shape_zones", "Edit zones shapes"), + ), + }, + ), + ] diff --git a/hat/menupermissions/models.py b/hat/menupermissions/models.py index 6c93500adc..ee9b0aaf01 100644 --- a/hat/menupermissions/models.py +++ b/hat/menupermissions/models.py @@ -39,6 +39,7 @@ _MAPPINGS = "iaso_mappings" _MODULES = "iaso_modules" _ORG_UNITS = "iaso_org_units" +_ORG_UNITS_READ = "iaso_org_units_read" _ORG_UNITS_TYPES = "iaso_org_unit_types" _ORG_UNITS_GROUPS = "iaso_org_unit_groups" _ORG_UNITS_CHANGE_REQUEST_REVIEW = "iaso_org_unit_change_request_review" @@ -130,6 +131,7 @@ MAPPINGS = _PREFIX + _MAPPINGS MODULES = _PREFIX + _MODULES ORG_UNITS = _PREFIX + _ORG_UNITS +ORG_UNITS_READ = _PREFIX + _ORG_UNITS_READ ORG_UNITS_TYPES = _PREFIX + _ORG_UNITS_TYPES ORG_UNITS_GROUPS = _PREFIX + _ORG_UNITS_GROUPS ORG_UNITS_CHANGE_REQUEST_REVIEW = _PREFIX + _ORG_UNITS_CHANGE_REQUEST_REVIEW @@ -236,6 +238,7 @@ class Meta: (_MODULES, _("modules")), (_COMPLETENESS, _("Complétude des données")), (_ORG_UNITS, _("Unités d'organisations")), + (_ORG_UNITS_READ, _("Lire les unités d'organisations")), (_REGISTRY_WRITE, _("Editer le Registre")), (_REGISTRY_READ, _("Lire le Registre")), (_LINKS, _("Correspondances sources")), diff --git a/iaso/api/org_units.py b/iaso/api/org_units.py index e022a0d3cf..c40c0670d9 100644 --- a/iaso/api/org_units.py +++ b/iaso/api/org_units.py @@ -31,6 +31,19 @@ # noinspection PyMethodMayBeStatic + + +class HasCreateOrUnitPermission(permissions.BasePermission): + def has_permission(self, request, view): + if not request.user.is_authenticated: + return False + + if not request.user.has_perm(permission.ORG_UNITS): + return False + + return True + + class HasOrgUnitPermission(permissions.BasePermission): def has_object_permission(self, request, view, obj): if not ( @@ -38,6 +51,7 @@ def has_object_permission(self, request, view, obj): and ( request.user.has_perm(permission.FORMS) or request.user.has_perm(permission.ORG_UNITS) + or request.user.has_perm(permission.ORG_UNITS_READ) or request.user.has_perm(permission.SUBMISSIONS) or request.user.has_perm(permission.REGISTRY_WRITE) or request.user.has_perm(permission.REGISTRY_READ) @@ -46,8 +60,10 @@ def has_object_permission(self, request, view, obj): ): return False - if obj.version.data_source.read_only and request.method != "GET": + read_only = request.user.has_perm(permission.ORG_UNITS_READ) and not request.user.has_perm(permission.ORG_UNITS) + if (read_only or obj.version.data_source.read_only) and request.method != "GET": return False + # TODO: can be handled with get_queryset() user_account = request.user.iaso_profile.account projects = obj.version.data_source.projects.all() @@ -569,7 +585,7 @@ def get_date(self, date: str) -> Union[datetime.date, None]: pass return None - @action(detail=False, methods=["POST"], permission_classes=[permissions.IsAuthenticated, HasOrgUnitPermission]) + @action(detail=False, methods=["POST"], permission_classes=[permissions.IsAuthenticated, HasCreateOrUnitPermission]) def create_org_unit(self, request): """This endpoint is used by the React frontend""" errors = [] From 92f706bb054512bbdd5632f5e3b7dc196e02916c Mon Sep 17 00:00:00 2001 From: HAKIZIMANA Franck Date: Thu, 15 Aug 2024 11:19:20 +0200 Subject: [PATCH 2/6] add test for orgUnits read permission --- iaso/tests/api/test_orgunits.py | 76 ++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/iaso/tests/api/test_orgunits.py b/iaso/tests/api/test_orgunits.py index 806b358532..27173227dd 100644 --- a/iaso/tests/api/test_orgunits.py +++ b/iaso/tests/api/test_orgunits.py @@ -128,6 +128,9 @@ def setUpTestData(cls): ) cls.yoda = cls.create_user_with_profile(username="yoda", account=star_wars, permissions=["iaso_org_units"]) + cls.user_read_permission = cls.create_user_with_profile( + username="user_read_permission", account=star_wars, permissions=["iaso_org_units_read"] + ) cls.luke = cls.create_user_with_profile( username="luke", account=star_wars, permissions=["iaso_org_units"], org_units=[jedi_council_endor] ) @@ -578,6 +581,28 @@ def assertCreated(self, createds: dict): self.assertTrue(set(createds.items()).issubset(set(diff.items()))) + def test_create_org_unit_with_read_permission(self): + self.client.force_authenticate(self.user_read_permission) + response = self.client.post( + f"/api/orgunits/create_org_unit/", + format="json", + data={ + "id": None, + "name": "Test ou", + "org_unit_type_id": self.jedi_council.pk, + "groups": [], + "sub_source": "", + "status": False, + "aliases": ["my alias"], + "validation_status": "NEW", + "parent_id": "", + "source_ref": "", + "creation_source": "dashboard", + "opening_date": "01-01-2024", + }, + ) + self.assertJSONResponse(response, 403) + def test_create_org_unit(self): self.client.force_authenticate(self.yoda) response = self.client.post( @@ -828,7 +853,6 @@ def test_edit_org_unit_retrieve_put(self): self.assertNotEqual(ou.updated_at, old_ou.updated_at) def test_edit_org_unit_unflag_reference_instance(self): - org_unit_type = self.jedi_council org_unit = self.jedi_council_corruscant form = self.reference_form instance = self.instance_related_to_reference_form @@ -859,6 +883,35 @@ def test_edit_org_unit_unflag_reference_instance(self): self.assertNotIn(instance, org_unit.reference_instances.all()) self.assertEqual(response.data["reference_instances"], []) + def test_edit_org_unit_reference_instance_read_permission(self): + org_unit = self.jedi_council_corruscant + form = self.reference_form + instance = self.instance_related_to_reference_form + + # Create a reference instance. + m.OrgUnitReferenceInstance.objects.create(org_unit=org_unit, instance=instance, form=form) + self.assertIn(instance, org_unit.reference_instances.all()) + + # GET /api/orgunits/id. + self.client.force_authenticate(self.user_read_permission) + response = self.client.get(f"/api/orgunits/{org_unit.id}/") + data = self.assertJSONResponse(response, 200) + + # PATCH /api/orgunits/id. + data.update( + { + "groups": [g["id"] for g in response.data["groups"]], + "reference_instance_id": instance.id, + "reference_instance_action": m.Instance.REFERENCE_UNFLAG_CODE, + } + ) + response = self.client.patch( + f"/api/orgunits/{org_unit.id}/", + format="json", + data=data, + ) + self.assertJSONResponse(response, 403) + def test_edit_org_unit_flag_reference_instance(self): """Retrieve an orgunit data and modify the reference_instance_id""" old_ou = self.jedi_council_corruscant @@ -934,6 +987,27 @@ def test_edit_org_unit_partial_update(self): self.assertEqual(ou.geom.wkt, MultiPolygon(Polygon([(0, 0), (0, 1), (1, 1), (0, 0)])).wkt) self.assertEqual(response.data["reference_instances"], []) + def test_edit_org_unit_partial_update_read_permission(self): + """Check tha we can only modify a part of the fille""" + ou = m.OrgUnit(version=self.sw_version_1) + ou.name = "test ou" + ou.source_ref = "b" + group_a = m.Group.objects.create(name="test group a") + group_b = m.Group.objects.create(name="test group b") + ou.geom = MultiPolygon(Polygon([(0, 0), (0, 1), (1, 1), (0, 0)])) + ou.save() + ou.groups.set([group_a, group_b]) + ou.save() + old_modification_date = ou.updated_at + self.client.force_authenticate(self.user_read_permission) + data = {"source_ref": "new source ref", "opening_date": "01-01-2024"} + response = self.client.patch( + f"/api/orgunits/{ou.id}/", + format="json", + data=data, + ) + self.assertJSONResponse(response, 403) + def test_edit_org_unit_edit_bad_group_fail(self): """Check for a previous bug if an org unit is already member of a bad group it couldn't be edited anymore from the interface From b9151e245f227eaf058e668685037967c4dd50f8 Mon Sep 17 00:00:00 2001 From: HAKIZIMANA Franck Date: Mon, 19 Aug 2024 14:07:31 +0200 Subject: [PATCH 3/6] apply iaso_org_units_read permission on frontend --- hat/assets/js/apps/Iaso/constants/routes.tsx | 4 +- .../components/SpeedDialInstance.tsx | 2 +- .../components/OrgUnitFiltersContainer.tsx | 34 +++++----- .../orgUnits/components/OrgUnitInfos.tsx | 64 ++++++++++-------- .../domains/orgUnits/components/TableList.tsx | 8 ++- .../orgUnitMap/OrgUnitMap/OrgUnitMap.tsx | 65 +++++++++++-------- .../orgUnits/history/LogCompareComponent.js | 50 +++++++------- .../components/OrgUnitsTypesDialog.tsx | 11 +++- 8 files changed, 138 insertions(+), 100 deletions(-) diff --git a/hat/assets/js/apps/Iaso/constants/routes.tsx b/hat/assets/js/apps/Iaso/constants/routes.tsx index 7e78ec71fc..1cf7c252be 100644 --- a/hat/assets/js/apps/Iaso/constants/routes.tsx +++ b/hat/assets/js/apps/Iaso/constants/routes.tsx @@ -152,14 +152,14 @@ export const mappingDetailPath = { export const orgUnitsPath = { baseUrl: baseUrls.orgUnits, routerUrl: `${baseUrls.orgUnits}/*`, - permissions: [Permission.ORG_UNITS], + permissions: [Permission.ORG_UNITS, Permission.ORG_UNITS_READ], element: , }; export const orgUnitsDetailsPath = { baseUrl: baseUrls.orgUnitDetails, routerUrl: `${baseUrls.orgUnitDetails}/*`, - permissions: [Permission.ORG_UNITS], + permissions: [Permission.ORG_UNITS, Permission.ORG_UNITS_READ], element: , }; diff --git a/hat/assets/js/apps/Iaso/domains/instances/components/SpeedDialInstance.tsx b/hat/assets/js/apps/Iaso/domains/instances/components/SpeedDialInstance.tsx index 2d8c51fd1f..eb6408fed2 100644 --- a/hat/assets/js/apps/Iaso/domains/instances/components/SpeedDialInstance.tsx +++ b/hat/assets/js/apps/Iaso/domains/instances/components/SpeedDialInstance.tsx @@ -118,7 +118,7 @@ const SpeedDialInstance: FunctionComponent = props => { const actions = [...baseActions, deleteRestore]; - if (!isGpsEqual) { + if (!isGpsEqual && userHasPermission(Permission.ORG_UNITS, currentUser)) { actions.unshift(editLocationWithInstanceGps); } diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitFiltersContainer.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitFiltersContainer.tsx index 8e10b1b8ee..e9c358653d 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitFiltersContainer.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitFiltersContainer.tsx @@ -35,6 +35,8 @@ import { Search } from '../types/search'; import { decodeSearch } from '../utils'; import MESSAGES from '../messages'; +import { DisplayIfUserHasPerm } from '../../../components/DisplayIfUserHasPerm'; +import { ORG_UNITS } from '../../../utils/permissions'; type Props = { params: OrgUnitParams; @@ -210,21 +212,23 @@ export const OrgUnitFiltersContainer: FunctionComponent = ({ ))} - - - + + + + + ({ speedDialTop: { @@ -191,42 +193,48 @@ export const OrgUnitInfos: FunctionComponent = ({ keyDateFrom="opening_date" keyDateTo="closed_date" onChangeDate={onChangeInfo} - dateFrom={orgUnitState.opening_date.value} - dateTo={orgUnitState.closed_date.value} + dateFrom={ + orgUnitState.opening_date.value as string | undefined + } + dateTo={ + orgUnitState.closed_date.value as string | undefined + } labelFrom={MESSAGES.openingDate} labelTo={MESSAGES.closingDate} marginTop={0} /> - - - {!isNewOrgunit && ( + + + + {!isNewOrgunit && ( + + )} - )} - - - + + + diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/components/TableList.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/components/TableList.tsx index e6f7dc1579..d7cbc059e3 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/components/TableList.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/components/TableList.tsx @@ -37,6 +37,9 @@ import MESSAGES from '../messages'; // HOOKS import { useGetOrgUnitsTableColumns } from '../hooks/useGetOrgUnitsTableColumns'; +import { userHasPermission } from '../../users/utils'; +import { ORG_UNITS } from '../../../utils/permissions'; +import { useCurrentUser } from '../../../utils/usersUtils'; // HOOKS type Props = { @@ -54,7 +57,7 @@ export const TableList: FunctionComponent = ({ saveMulti, }) => { const { formatMessage } = useSafeIntl(); - + const currentUser = useCurrentUser(); const [multiActionPopupOpen, setMultiActionPopupOpen] = useState(false); const [selection, setSelection] = useState>( @@ -69,7 +72,8 @@ export const TableList: FunctionComponent = ({ const columns = useGetOrgUnitsTableColumns(searches); const multiEditDisabled = - !selection.selectAll && selection.selectedItems.length === 0; + !userHasPermission(ORG_UNITS, currentUser) || + (!selection.selectAll && selection.selectedItems.length === 0); const handleTableSelection = useCallback( (selectionType, items = [], totalCount = 0) => { diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/components/orgUnitMap/OrgUnitMap/OrgUnitMap.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/components/orgUnitMap/OrgUnitMap/OrgUnitMap.tsx index 1253352d29..39a6d8f900 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/components/orgUnitMap/OrgUnitMap/OrgUnitMap.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/components/orgUnitMap/OrgUnitMap/OrgUnitMap.tsx @@ -49,6 +49,7 @@ import { CustomZoomControl } from '../../../../../components/maps/tools/CustomZo import * as Permission from '../../../../../utils/permissions'; import { DisplayIfUserHasPerm } from '../../../../../components/DisplayIfUserHasPerm'; import { InnerDrawer } from '../../../../../components/nav/InnerDrawer/Index'; +import { userHasPermission } from '../../../../users/utils'; export const zoom = 5; export const padding = [75, 75]; @@ -382,6 +383,12 @@ export const OrgUnitMap: FunctionComponent = ({ latitude: [], longitude: [], }); + + const hasEditPermission = userHasPermission( + Permission.ORG_UNITS, + currentUser, + ); + return ( = ({ } editOptionComponent={ - toggleEditShape(keyValue)} - toggleDeleteShape={keyValue => - toggleDeleteShape(keyValue) - } - isCreatingMarker={isCreatingMarker} - toggleAddShape={keyValue => toggleAddShape(keyValue)} - toggleAddMarker={() => { - setIsCreatingMarker(!isCreatingMarker); - state.locationGroup.value.toggleDrawMarker( - !isCreatingMarker, - ); - }} - addShape={shapeType => addShape(shapeType)} - onChangeLocation={latLong => { - setIsCreatingMarker(false); - onChangeLocation(latLong); - }} - errorsCoordinates={errorsCoordinates} - setErrorsCoordinates={setErrorsCoordinates} - /> + hasEditPermission && ( + + toggleEditShape(keyValue) + } + toggleDeleteShape={keyValue => + toggleDeleteShape(keyValue) + } + isCreatingMarker={isCreatingMarker} + toggleAddShape={keyValue => + toggleAddShape(keyValue) + } + toggleAddMarker={() => { + setIsCreatingMarker(!isCreatingMarker); + state.locationGroup.value.toggleDrawMarker( + !isCreatingMarker, + ); + }} + addShape={shapeType => addShape(shapeType)} + onChangeLocation={latLong => { + setIsCreatingMarker(false); + onChangeLocation(latLong); + }} + errorsCoordinates={errorsCoordinates} + setErrorsCoordinates={setErrorsCoordinates} + /> + ) } commentsOptionComponent={ ({ ...commonStyles(theme), @@ -183,40 +185,42 @@ const LogCompareComponent = ({ alignItems="center" justifyContent="center" > - - goToRevision(l)} - /> - - {isNewValue && ( + - goToRevision({ - fields: differenceArray[i], - }) - } + confirm={() => goToRevision(l)} /> - )} + {isNewValue && ( + + + goToRevision({ + fields: differenceArray[i], + }) + } + /> + + )} + ); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/components/OrgUnitsTypesDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/components/OrgUnitsTypesDialog.tsx index f5dc464d45..67629b795c 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/components/OrgUnitsTypesDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/components/OrgUnitsTypesDialog.tsx @@ -20,7 +20,10 @@ import { useGetFormsByProjects } from '../../../instances/hooks'; import ConfirmCancelDialogComponent from '../../../../components/dialogs/ConfirmCancelDialogComponent'; import InputComponent from '../../../../components/forms/InputComponent'; import MESSAGES from '../messages'; -import { userHasPermission } from '../../../users/utils'; +import { + userHasOneOfPermissions, + userHasPermission, +} from '../../../users/utils'; import { useFormState } from '../../../../hooks/form'; import { commaSeparatedIdsToArray, @@ -223,8 +226,10 @@ export const OrgUnitsTypesDialog: FunctionComponent = ({ [formState, formatMessage, saveType, setFieldErrors], ); const hasPermission = - userHasPermission(Permission.ORG_UNITS, currentUser) && - userHasPermission(Permission.FORMS, currentUser); + userHasOneOfPermissions( + [Permission.ORG_UNITS, Permission.ORG_UNITS_READ], + currentUser, + ) && userHasPermission(Permission.FORMS, currentUser); const resetForm = () => { setFormState(mapOrgUnitType(orgUnitType)); From 728fcb1dba3c45c81f931936893a2679fdeb5dff Mon Sep 17 00:00:00 2001 From: HAKIZIMANA Franck Date: Wed, 21 Aug 2024 17:57:17 +0200 Subject: [PATCH 4/6] fix data source, groups, mobile org_units and source_versions by adding iaso_org_units_read permission --- iaso/api/data_sources.py | 3 ++- iaso/api/groups.py | 4 ++-- iaso/api/mobile/org_units.py | 1 + iaso/api/source_versions.py | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/iaso/api/data_sources.py b/iaso/api/data_sources.py index b1d82b3e47..513e4f1e06 100644 --- a/iaso/api/data_sources.py +++ b/iaso/api/data_sources.py @@ -207,6 +207,7 @@ def has_permission(self, request, view): read_perms = ( permission.MAPPINGS, permission.ORG_UNITS, + permission.ORG_UNITS_READ, permission.LINKS, permission.SOURCES, ) @@ -228,7 +229,7 @@ class DataSourceViewSet(ModelViewSet): This API is restricted to authenticated users: Read permission are restricted to user with at least one of the "{permission.SOURCES}", - "{permission.MAPPINGS}","{permission.ORG_UNITS}", and "{permission.LINKS}" permissions + "{permission.MAPPINGS}","{permission.ORG_UNITS}","{permission.ORG_UNITS_READ}" and "{permission.LINKS}" permissions Write permission are restricted to user having the "{permission.SOURCES}" permissions. GET /api/datasources/ diff --git a/iaso/api/groups.py b/iaso/api/groups.py index 87ae01491e..b9589385e8 100644 --- a/iaso/api/groups.py +++ b/iaso/api/groups.py @@ -80,7 +80,7 @@ class Meta: class GroupsViewSet(ModelViewSet): f"""Groups API - This API is restricted to users having the "{permission.ORG_UNITS}" and "{permission.COMPLETENESS_STATS}" permission + This API is restricted to users having the "{permission.ORG_UNITS}", "{permission.ORG_UNITS_READ}" and "{permission.COMPLETENESS_STATS}" permission GET /api/groups/ GET /api/groups/ @@ -91,7 +91,7 @@ class GroupsViewSet(ModelViewSet): permission_classes = [ permissions.IsAuthenticated, - HasPermission(permission.ORG_UNITS, permission.COMPLETENESS_STATS), # type: ignore + HasPermission(permission.ORG_UNITS, permission.ORG_UNITS_READ, permission.COMPLETENESS_STATS), # type: ignore HasGroupPermission, ] serializer_class = GroupSerializer diff --git a/iaso/api/mobile/org_units.py b/iaso/api/mobile/org_units.py index a2b7359200..dae90256b3 100644 --- a/iaso/api/mobile/org_units.py +++ b/iaso/api/mobile/org_units.py @@ -129,6 +129,7 @@ def has_object_permission(self, request, view, obj): and ( request.user.has_perm(permission.FORMS) or request.user.has_perm(permission.ORG_UNITS) + or request.user.has_perm(permission.ORG_UNITS_READ) or request.user.has_perm(permission.SUBMISSIONS) ) ): diff --git a/iaso/api/source_versions.py b/iaso/api/source_versions.py index b92796ce4e..e16555611c 100644 --- a/iaso/api/source_versions.py +++ b/iaso/api/source_versions.py @@ -69,7 +69,7 @@ class SourceVersionViewSet(ModelViewSet): f"""Data source API This API is restricted to authenticated users having at least one of the "{permission.MAPPINGS}", - "{permission.ORG_UNITS}", and "{permission.LINKS}" permissions + "{permission.ORG_UNITS}","{permission.ORG_UNITS_READ}", and "{permission.LINKS}" permissions GET /api/sourceversions/ GET /api/sourceversions/ @@ -77,7 +77,7 @@ class SourceVersionViewSet(ModelViewSet): permission_classes = [ permissions.IsAuthenticated, - HasPermission(permission.MAPPINGS, permission.ORG_UNITS, permission.LINKS), # type: ignore + HasPermission(permission.MAPPINGS, permission.ORG_UNITS, permission.ORG_UNITS_READ, permission.LINKS), # type: ignore ] serializer_class = SourceVersionSerializer results_key = "versions" From 5fbf9d013e2eb7b80ce6df35f0e4d29d3fdeb05a Mon Sep 17 00:00:00 2001 From: HAKIZIMANA Franck Date: Thu, 22 Aug 2024 11:14:18 +0200 Subject: [PATCH 5/6] refactor some tests --- iaso/tests/api/test_orgunits.py | 86 +++++++++++---------------------- 1 file changed, 27 insertions(+), 59 deletions(-) diff --git a/iaso/tests/api/test_orgunits.py b/iaso/tests/api/test_orgunits.py index 27173227dd..8e260141b8 100644 --- a/iaso/tests/api/test_orgunits.py +++ b/iaso/tests/api/test_orgunits.py @@ -581,9 +581,8 @@ def assertCreated(self, createds: dict): self.assertTrue(set(createds.items()).issubset(set(diff.items()))) - def test_create_org_unit_with_read_permission(self): - self.client.force_authenticate(self.user_read_permission) - response = self.client.post( + def set_up_org_unit_creation(self): + return self.client.post( f"/api/orgunits/create_org_unit/", format="json", data={ @@ -601,28 +600,17 @@ def test_create_org_unit_with_read_permission(self): "opening_date": "01-01-2024", }, ) + + def test_create_org_unit_with_read_permission(self): + """Check that we cannot create org unit with org units read only permission""" + self.client.force_authenticate(self.user_read_permission) + response = self.set_up_org_unit_creation() self.assertJSONResponse(response, 403) def test_create_org_unit(self): + """Check that we can create org unit with only org units management permission""" self.client.force_authenticate(self.yoda) - response = self.client.post( - f"/api/orgunits/create_org_unit/", - format="json", - data={ - "id": None, - "name": "Test ou", - "org_unit_type_id": self.jedi_council.pk, - "groups": [], - "sub_source": "", - "status": False, - "aliases": ["my alias"], - "validation_status": "NEW", - "parent_id": "", - "source_ref": "", - "creation_source": "dashboard", - "opening_date": "01-01-2024", - }, - ) + response = self.set_up_org_unit_creation() self.assertJSONResponse(response, 200) json = response.json() self.assertValidOrgUnitData(json) @@ -852,7 +840,7 @@ def test_edit_org_unit_retrieve_put(self): self.assertEqual(ou.created_at, old_ou.created_at) self.assertNotEqual(ou.updated_at, old_ou.updated_at) - def test_edit_org_unit_unflag_reference_instance(self): + def set_up_edit_org_flag_reference_instance(self, user): org_unit = self.jedi_council_corruscant form = self.reference_form instance = self.instance_related_to_reference_form @@ -862,7 +850,7 @@ def test_edit_org_unit_unflag_reference_instance(self): self.assertIn(instance, org_unit.reference_instances.all()) # GET /api/orgunits/id. - self.client.force_authenticate(self.yoda) + self.client.force_authenticate(user) response = self.client.get(f"/api/orgunits/{org_unit.id}/") data = self.assertJSONResponse(response, 200) @@ -874,6 +862,11 @@ def test_edit_org_unit_unflag_reference_instance(self): "reference_instance_action": m.Instance.REFERENCE_UNFLAG_CODE, } ) + + return org_unit, instance, data + + def test_edit_org_unit_unflag_reference_instance(self): + org_unit, instance, data = self.set_up_edit_org_flag_reference_instance(self.yoda) response = self.client.patch( f"/api/orgunits/{org_unit.id}/", format="json", @@ -884,27 +877,7 @@ def test_edit_org_unit_unflag_reference_instance(self): self.assertEqual(response.data["reference_instances"], []) def test_edit_org_unit_reference_instance_read_permission(self): - org_unit = self.jedi_council_corruscant - form = self.reference_form - instance = self.instance_related_to_reference_form - - # Create a reference instance. - m.OrgUnitReferenceInstance.objects.create(org_unit=org_unit, instance=instance, form=form) - self.assertIn(instance, org_unit.reference_instances.all()) - - # GET /api/orgunits/id. - self.client.force_authenticate(self.user_read_permission) - response = self.client.get(f"/api/orgunits/{org_unit.id}/") - data = self.assertJSONResponse(response, 200) - - # PATCH /api/orgunits/id. - data.update( - { - "groups": [g["id"] for g in response.data["groups"]], - "reference_instance_id": instance.id, - "reference_instance_action": m.Instance.REFERENCE_UNFLAG_CODE, - } - ) + org_unit, _, data = self.set_up_edit_org_flag_reference_instance(self.user_read_permission) response = self.client.patch( f"/api/orgunits/{org_unit.id}/", format="json", @@ -958,8 +931,7 @@ def test_edit_org_unit_flag_wrong_reference_instance(self): self.assertEqual(old_modification_date, old_ou.updated_at) self.assertNotIn(self.instance_not_related_to_reference_form, old_ou.reference_instances.all()) - def test_edit_org_unit_partial_update(self): - """Check tha we can only modify a part of the fille""" + def set_up_org_unit_partial_update(self): ou = m.OrgUnit(version=self.sw_version_1) ou.name = "test ou" ou.source_ref = "b" @@ -970,8 +942,14 @@ def test_edit_org_unit_partial_update(self): ou.groups.set([group_a, group_b]) ou.save() old_modification_date = ou.updated_at - self.client.force_authenticate(self.yoda) data = {"source_ref": "new source ref", "opening_date": "01-01-2024"} + + return ou, group_a, group_b, old_modification_date, data + + def test_edit_org_unit_partial_update(self): + """Check that we can only modify a part of the file with org units management permission""" + ou, group_a, group_b, old_modification_date, data = self.set_up_org_unit_partial_update() + self.client.force_authenticate(self.yoda) response = self.client.patch( f"/api/orgunits/{ou.id}/", format="json", @@ -988,19 +966,9 @@ def test_edit_org_unit_partial_update(self): self.assertEqual(response.data["reference_instances"], []) def test_edit_org_unit_partial_update_read_permission(self): - """Check tha we can only modify a part of the fille""" - ou = m.OrgUnit(version=self.sw_version_1) - ou.name = "test ou" - ou.source_ref = "b" - group_a = m.Group.objects.create(name="test group a") - group_b = m.Group.objects.create(name="test group b") - ou.geom = MultiPolygon(Polygon([(0, 0), (0, 1), (1, 1), (0, 0)])) - ou.save() - ou.groups.set([group_a, group_b]) - ou.save() - old_modification_date = ou.updated_at + """Check that we can only modify a part of the file with org units read only permission""" + ou, _, _, _, data = self.set_up_org_unit_partial_update() self.client.force_authenticate(self.user_read_permission) - data = {"source_ref": "new source ref", "opening_date": "01-01-2024"} response = self.client.patch( f"/api/orgunits/{ou.id}/", format="json", From 2030e98ccfea9c8894f03305e8787c81a9fcfffe Mon Sep 17 00:00:00 2001 From: HAKIZIMANA Franck Date: Thu, 22 Aug 2024 15:22:15 +0200 Subject: [PATCH 6/6] disabled org Units info inputs when the user has not org unit management permission --- .../Iaso/components/filters/DatesRange.d.ts | 3 +- .../Iaso/components/filters/DatesRange.js | 5 +++ .../components/ActionTableColumnComponent.js | 43 +++++++++---------- .../orgUnits/components/OrgUnitInfos.tsx | 40 ++++++++++++++--- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/hat/assets/js/apps/Iaso/components/filters/DatesRange.d.ts b/hat/assets/js/apps/Iaso/components/filters/DatesRange.d.ts index 7d2fc28726..f91452b5de 100644 --- a/hat/assets/js/apps/Iaso/components/filters/DatesRange.d.ts +++ b/hat/assets/js/apps/Iaso/components/filters/DatesRange.d.ts @@ -1,10 +1,10 @@ // DatesRange.d.ts import { ComponentType } from 'react'; -import { IntlShape } from 'react-intl'; interface DatesRangeProps { dateFrom?: string; dateTo?: string; + // eslint-disable-next-line no-unused-vars onChangeDate: (key: string, value: any) => void; labelTo?: { id: string; defaultMessage: string }; labelFrom?: { id: string; defaultMessage: string }; @@ -19,6 +19,7 @@ interface DatesRangeProps { marginTop?: number; dateFromRequired?: boolean; dateToRequired?: boolean; + disabled?: boolean; } declare const DatesRange: ComponentType; diff --git a/hat/assets/js/apps/Iaso/components/filters/DatesRange.js b/hat/assets/js/apps/Iaso/components/filters/DatesRange.js index fd9ca9cb3f..ae0f8723dc 100644 --- a/hat/assets/js/apps/Iaso/components/filters/DatesRange.js +++ b/hat/assets/js/apps/Iaso/components/filters/DatesRange.js @@ -75,6 +75,7 @@ const DatesRange = ({ marginTop, dateFromRequired = false, dateToRequired = false, + disabled = false, }) => { const classes = useStyles(); const [from, setFrom] = useState(dateFrom); @@ -146,6 +147,7 @@ const DatesRange = ({ handleChange(keyDateFrom, date); }} error={errors[0].length > 0} + disabled={disabled} /> {dateFrom && ( @@ -201,6 +203,7 @@ const DatesRange = ({ handleChange(keyDateTo, date); }} error={errors[1].length > 0} + disabled={disabled} /> {dateTo && ( @@ -239,6 +242,7 @@ DatesRange.defaultProps = { marginTop: 2, dateFromRequired: false, dateToRequired: false, + disabled: false, }; DatesRange.propTypes = { @@ -259,6 +263,7 @@ DatesRange.propTypes = { marginTop: PropTypes.number, dateFromRequired: PropTypes.bool, dateToRequired: PropTypes.bool, + disabled: PropTypes.bool, }; const DatesRangeIntl = injectIntl(DatesRange); diff --git a/hat/assets/js/apps/Iaso/domains/instances/components/ActionTableColumnComponent.js b/hat/assets/js/apps/Iaso/domains/instances/components/ActionTableColumnComponent.js index c59da2582b..65e73bfc02 100644 --- a/hat/assets/js/apps/Iaso/domains/instances/components/ActionTableColumnComponent.js +++ b/hat/assets/js/apps/Iaso/domains/instances/components/ActionTableColumnComponent.js @@ -9,8 +9,8 @@ import omit from 'lodash/omit'; import { DialogContentText } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { useSaveOrgUnit } from '../../orgUnits/hooks'; -import { baseUrls } from '../../../constants/urls'; -import { userHasPermission } from '../../users/utils'; +import { baseUrls } from '../../../constants/urls.ts'; +import { userHasOneOfPermissions, userHasPermission } from '../../users/utils'; import { useCurrentUser } from '../../../utils/usersUtils.ts'; import MESSAGES from '../messages'; import { REFERENCE_FLAG_CODE, REFERENCE_UNFLAG_CODE } from '../constants'; @@ -119,7 +119,10 @@ const ActionTableColumnComponent = ({ settings }) => { const showOrgUnitButton = settings.row.original.org_unit && - userHasPermission('iaso_org_units', user) && + userHasOneOfPermissions( + [Permission.ORG_UNITS, Permission.ORG_UNITS_READ], + user, + ) && userHasPermission(Permission.SUBMISSIONS_UPDATE, user); const confirmCancelTitleMessage = isItLinked => { @@ -189,25 +192,21 @@ const ActionTableColumnComponent = ({ settings }) => { )} {settings.row.original.is_locked && - userHasPermission(Permission.SUBMISSIONS_UPDATE, user) && ( - <> - {settings.row.original.can_user_modify ? ( - ( - - )} - tooltipMessage={MESSAGES.lockedCanModify} - /> - ) : ( - - )} - - )} + userHasPermission(Permission.SUBMISSIONS_UPDATE, user) && + (settings.row.original.can_user_modify ? ( + } + tooltipMessage={MESSAGES.lockedCanModify} + /> + ) : ( + + ))} ); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitInfos.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitInfos.tsx index 89e5519fed..15228ed639 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitInfos.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/components/OrgUnitInfos.tsx @@ -26,6 +26,8 @@ import { OrgUnitMultiReferenceInstances } from './OrgUnitMultiReferenceInstances import { useGetOrgUnit } from './TreeView/requests'; import { DisplayIfUserHasPerm } from '../../../components/DisplayIfUserHasPerm'; import { ORG_UNITS } from '../../../utils/permissions'; +import { userHasPermission } from '../../users/utils'; +import { useCurrentUser } from '../../../utils/usersUtils'; const useStyles = makeStyles(theme => ({ speedDialTop: { @@ -34,6 +36,17 @@ const useStyles = makeStyles(theme => ({ marginLeft: { marginLeft: `${theme.spacing(2)} !important`, }, + divAliasWrapper: { + position: 'relative', + }, + divAliasOverlay: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + cursor: 'not-allowed', + }, })); type Props = { @@ -90,6 +103,8 @@ export const OrgUnitInfos: FunctionComponent = ({ ? `${orgUnitState.parent.value.id}` : undefined, ); + const currentUser = useCurrentUser(); + const hasManagementPermission = userHasPermission(ORG_UNITS, currentUser); return ( @@ -101,6 +116,7 @@ export const OrgUnitInfos: FunctionComponent = ({ value={orgUnitState.name.value} errors={orgUnitState.name.errors} label={MESSAGES.name} + disabled={!hasManagementPermission} /> = ({ value: t.id, }))} label={MESSAGES.org_unit_type_id} + disabled={!hasManagementPermission} /> = ({ value: g.id, }))} label={MESSAGES.groups} + disabled={!hasManagementPermission} /> - - +
+ + {!hasManagementPermission && ( +
+ )} +
@@ -159,6 +181,7 @@ export const OrgUnitInfos: FunctionComponent = ({ label={MESSAGES.status} loading={isLoadingValidationStatusOptions} options={validationStatusOptions || []} + disabled={!hasManagementPermission} /> = ({ value={orgUnitState.source_ref.value || ''} onChange={onChangeInfo} errors={orgUnitState.source_ref.errors} + disabled={!hasManagementPermission} /> = ({ source={orgUnit.source_id} initialSelection={parentOrgunit} resetTrigger={resetTrigger} + disabled={!hasManagementPermission} /> = ({ labelFrom={MESSAGES.openingDate} labelTo={MESSAGES.closingDate} marginTop={0} + disabled={!hasManagementPermission} />