From 18544203e7d7c9075f16003b363b791e5250fbf6 Mon Sep 17 00:00:00 2001 From: Simon K <6615834+simon-20@users.noreply.github.com> Date: Mon, 11 May 2026 12:02:33 +0100 Subject: [PATCH 1/4] test: adjust tests now that PROVIDER_ADMINs hidden Previously anyone logged in as a PROVIDER_ADMIN saw in the list of a reporting org's users all the other provider admins for that org, but for now this is being removed until we have per-tool handling. --- tests/integration/test_reporting_org_routes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/integration/test_reporting_org_routes.py b/tests/integration/test_reporting_org_routes.py index 54c92b2..58ca32f 100644 --- a/tests/integration/test_reporting_org_routes.py +++ b/tests/integration/test_reporting_org_routes.py @@ -713,8 +713,6 @@ def test_reporting_org_list_datasets_detail( "698e0c1f-4e80-faa9-6533-68de801d1735", "bea511d3-c7a7-4097-55ed-68de81e94921", "7625122c-f752-40dc-a577-5cb49e13de2a", - "b46b88bd-05e6-4cb8-8b6a-a2c47fcd666d", - "5c633101-42be-47ac-81e7-43d6ecb503e3", }, ), (0, "da17734d-3926-47ef-8563-8a1b0247ed11", True, []), From 5937c3ff14583d50a56a5e82edf22d75ed20edce Mon Sep 17 00:00:00 2001 From: Simon K <6615834+simon-20@users.noreply.github.com> Date: Mon, 11 May 2026 12:39:44 +0100 Subject: [PATCH 2/4] fix: filter out provider admins from user list This commit hides provider admins from the list of users returned for reporting orgs for all except superadmins. --- .../routers/reporting_orgs.py | 103 +++++++++--------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/src/register_your_data_api/routers/reporting_orgs.py b/src/register_your_data_api/routers/reporting_orgs.py index 71b28a9..d1e0f1f 100644 --- a/src/register_your_data_api/routers/reporting_orgs.py +++ b/src/register_your_data_api/routers/reporting_orgs.py @@ -488,76 +488,73 @@ def get_reporting_org_users( crm: SuiteCRM = context.suitecrm_client_factory.get_client() - current_user_role_for_ro = user.validator.get_user_role_for_reporting_org(org_id) - users_for_org_from_suitecrm = crm.get_relationship("Accounts", str(org_id), "Contacts") - users_for_org_from_fga = context._fga_provider.get_user_associations_for_org(org_id) + users_for_org_from_fga = context.fine_grained_auth_provider.get_user_associations_for_org(org_id) user_ids_from_fga = {str(u.user) for u in users_for_org_from_fga} - names_emails_from_suitecrm = { + # names and emails for non-provider admins come from SuiteCRM + names_emails_for_org_users = { u["id"]: (u["attributes"]["last_name"], u["attributes"]["email1"]) for u in users_for_org_from_suitecrm["data"] if u["id"] in user_ids_from_fga } - # iterate over the provider admins, and add their names/emails to the dict - for provider_admin in users_for_org_from_fga: - if provider_admin.role == fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN: - filters = Filter() - filters.equal("id", str(provider_admin.user)) - crm_user = crm.get_records("Contacts", fields=["last_name", "email1"], filters=filters) - if "data" in crm_user and len(crm_user["data"]) > 0: - names_emails_from_suitecrm[crm_user["data"][0]["id"]] = ( - crm_user["data"][0]["attributes"]["last_name"], - crm_user["data"][0]["attributes"]["email1"], - ) - - user_ids_in_fga_not_suitecrm = user_ids_from_fga - {*names_emails_from_suitecrm.keys()} - if user_ids_in_fga_not_suitecrm: - if any( - [ - u.role != fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN and u.user == uuid.UUID(user_id) - for u in users_for_org_from_fga - for user_id in user_ids_in_fga_not_suitecrm - ] - ): - trace_id: uuid.UUID = uuid.uuid4() - raise RYDUserException( - user.user_id_crm, - user.client_id, - 500, - app_msg=( - f"GET request to reporting-orgs/{org_id}/users has found " - f"users that are not associated with that org in SuiteCRM: {user_ids_in_fga_not_suitecrm}. " - f"Trace id: {trace_id}" - ), - audit_msg=( - f"GET request to reporting-orgs/{org_id}/users by user id: {user.user_id_crm} " - f"but the following users associated with reporting org {org_id} in the FGA data " - f"store are not associated with that org in SuiteCRM: {user_ids_in_fga_not_suitecrm}. " - f"Trace id: {trace_id}" - ), - public_msg=( - "There is a problem getting the user list associated with this organisation. " - f"Please contact IATI Support quoting this error trace id: {trace_id}" - ), - ) + # names and emails for provider admins come from FGA DB, so iterate over the provider admins to add them + # BUT, we only pull these details in when needed, to avoid making extra + # calls to SuiteCRM for every request when the data isn't needed + if user.validator.is_superadmin: + for user_for_org in users_for_org_from_fga: + if user_for_org.role == fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN: + filters = Filter() + filters.equal("id", str(user_for_org.user)) + crm_user = crm.get_records("Contacts", fields=["last_name", "email1"], filters=filters) + if "data" in crm_user and len(crm_user["data"]) > 0: + names_emails_for_org_users[crm_user["data"][0]["id"]] = ( + crm_user["data"][0]["attributes"]["last_name"], + crm_user["data"][0]["attributes"]["email1"], + ) + + user_ids_in_fga_not_suitecrm = user_ids_from_fga - {*names_emails_for_org_users.keys()} + if user_ids_in_fga_not_suitecrm and any( + [ + u.role != fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN and u.user == uuid.UUID(user_id) + for u in users_for_org_from_fga + for user_id in user_ids_in_fga_not_suitecrm + ] + ): + trace_id: uuid.UUID = uuid.uuid4() + raise RYDUserException( + user.user_id_crm, + user.client_id, + 500, + app_msg=( + f"GET request to reporting-orgs/{org_id}/users has found " + f"users that are not associated with that org in SuiteCRM: {user_ids_in_fga_not_suitecrm}. " + f"Trace id: {trace_id}" + ), + audit_msg=( + f"GET request to reporting-orgs/{org_id}/users by user id: {user.user_id_crm} " + f"but the following users associated with reporting org {org_id} in the FGA data " + f"store are not associated with that org in SuiteCRM: {user_ids_in_fga_not_suitecrm}. " + f"Trace id: {trace_id}" + ), + public_msg=( + "There is a problem getting the user list associated with this organisation. " + f"Please contact IATI Support quoting this error trace id: {trace_id}" + ), + ) users_for_org = [ CRMUser( id=str(u.user), - name=names_emails_from_suitecrm[str(u.user)][0], - email=names_emails_from_suitecrm[str(u.user)][1], + name=names_emails_for_org_users[str(u.user)][0], + email=names_emails_for_org_users[str(u.user)][1], role=get_fga_role_as_str(u.role), ) for u in users_for_org_from_fga - if ( - u.role != fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN - or current_user_role_for_ro == fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN - or user.validator.is_superadmin - ) + if (u.role != fga_models.FineGrainedAuthorisationRole.PROVIDER_ADMIN or user.validator.is_superadmin) ] users_for_org.sort(key=lambda u: u.name.lower()) From ecbd17e23c7f93c8f7251914f63c76869839ad87 Mon Sep 17 00:00:00 2001 From: Simon K <6615834+simon-20@users.noreply.github.com> Date: Mon, 11 May 2026 12:39:53 +0100 Subject: [PATCH 3/4] build: bump version number --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cfc36ff..fc18690 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "register-your-data-api" -version = "0.3.6" +version = "0.3.7" requires-python = ">= 3.12.11" readme = "README.md" authors = [{name="IATI Secretariat", email="support@iatistandard.org"}] From 89068e7ebbd12f66c4ff8633a1ae061aae189b2f Mon Sep 17 00:00:00 2001 From: Simon K <6615834+simon-20@users.noreply.github.com> Date: Mon, 11 May 2026 12:40:43 +0100 Subject: [PATCH 4/4] docs: update CHANGELOG - filter provider admins Filter provider admins from a reporting org's user list for other provider admins (still viewable by superadmins). --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c156d1..006fd76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security +## [0.3.7] - 2026-05-11 + +### Fixed + +- Filtered out the provider admins from the list of users returned for reporting orgs. + ## [0.3.6] - 2026-05-06 ### Changed