From a20c77639d2e9c5814018b396a65156ae59213e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=A4mmer?= Date: Mon, 9 Feb 2026 09:58:42 +0100 Subject: [PATCH 1/4] [MODAUD-296] Add user audit enabled setting Implements settings infrastructure for user audit logging: - Add USER setting group (audit.users) to enum and database - Add USER_RECORDS_ENABLED setting (defaults to false) - Add permissions for user audit configuration: - audit.config.groups.settings.audit.users.collection.get - audit.config.groups.settings.audit.users.enabled.item.put - Add integration tests for user settings CRUD and permissions This creates the configuration foundation for future user event processing. The enabled flag defaults to false (disabled) and can be toggled via the existing /audit/config/groups API. --- descriptors/ModuleDescriptor-template.json | 10 +++++ .../folio/services/configuration/Setting.java | 3 +- .../services/configuration/SettingGroup.java | 3 +- .../config/populate_setting_group_table.sql | 5 ++- .../config/populate_setting_table.sql | 10 +++++ .../folio/rest/impl/AuditConfigAPITest.java | 39 ++++++++++++++++++- 6 files changed, 65 insertions(+), 5 deletions(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index d419fb9b..1a2d3530 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -480,6 +480,16 @@ "displayName": "Audit Configuration - enable/disable audit authority records", "description": "Enable/disable audit authority records" }, + { + "permissionName": "audit.config.groups.settings.audit.users.collection.get", + "displayName": "Audit Configuration - get settings for audit users group", + "description": "Get settings for audit users group" + }, + { + "permissionName": "audit.config.groups.settings.audit.users.enabled.item.put", + "displayName": "Audit Configuration - enable/disable audit user records", + "description": "Enable/disable audit user records" + }, { "permissionName": "audit.inventory.instance.collection.get", "displayName": "Inventory Audit - get instance events", diff --git a/mod-audit-server/src/main/java/org/folio/services/configuration/Setting.java b/mod-audit-server/src/main/java/org/folio/services/configuration/Setting.java index 21b20023..d9cff4ea 100644 --- a/mod-audit-server/src/main/java/org/folio/services/configuration/Setting.java +++ b/mod-audit-server/src/main/java/org/folio/services/configuration/Setting.java @@ -10,7 +10,8 @@ public enum Setting { AUTHORITY_RECORDS_RETENTION_PERIOD(SettingGroup.AUTHORITY, SettingKey.RETENTION_PERIOD), INVENTORY_RECORDS_RETENTION_PERIOD(SettingGroup.INVENTORY, SettingKey.RETENTION_PERIOD), INVENTORY_RECORDS_ENABLED(SettingGroup.INVENTORY, SettingKey.ENABLED), - AUTHORITY_RECORDS_ENABLED(SettingGroup.AUTHORITY, SettingKey.ENABLED); + AUTHORITY_RECORDS_ENABLED(SettingGroup.AUTHORITY, SettingKey.ENABLED), + USER_RECORDS_ENABLED(SettingGroup.USER, SettingKey.ENABLED); private final SettingGroup group; private final SettingKey key; diff --git a/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java b/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java index bdca97a3..e285056d 100644 --- a/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java +++ b/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java @@ -6,7 +6,8 @@ public enum SettingGroup { AUTHORITY("audit.authority"), - INVENTORY("audit.inventory"); + INVENTORY("audit.inventory"), + USER("audit.users"); private final String id; diff --git a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql index 6599c344..a3a5b3ba 100644 --- a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql +++ b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql @@ -4,5 +4,8 @@ VALUES ('audit.inventory', 'Group of configurations for audit of inventory records: instances, holdings, items, etc.'), ('audit.authority', 'Authority Audit Configuration', - 'Group of configurations for audit of authority records') + 'Group of configurations for audit of authority records'), + ('audit.users', + 'User Audit Configuration', + 'Group of configurations for audit of user records') ON CONFLICT (id) DO NOTHING; diff --git a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql index 08d3605e..6e172baa 100644 --- a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql +++ b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql @@ -58,5 +58,15 @@ VALUES ('audit.inventory.records.page.size', now(), '00000000-0000-0000-0000-000000000000', now(), + '00000000-0000-0000-0000-000000000000'), + ('audit.users.enabled', + 'enabled', + to_jsonb(false::boolean), + 'BOOLEAN', + 'Defines if the user audit is enabled', + 'audit.users', + now(), + '00000000-0000-0000-0000-000000000000', + now(), '00000000-0000-0000-0000-000000000000') ON CONFLICT (id) DO NOTHING; diff --git a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java index 41a68c25..51699614 100644 --- a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java +++ b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java @@ -6,6 +6,7 @@ import static org.folio.dao.configuration.SettingValueType.INTEGER; import static org.folio.services.configuration.SettingGroup.AUTHORITY; import static org.folio.services.configuration.SettingGroup.INVENTORY; +import static org.folio.services.configuration.SettingGroup.USER; import static org.folio.services.configuration.SettingKey.ENABLED; import static org.folio.services.configuration.SettingKey.RECORDS_PAGE_SIZE; import static org.folio.services.configuration.SettingKey.RETENTION_PERIOD; @@ -43,12 +44,18 @@ public class AuditConfigAPITest extends ApiTestBase { "audit.config.groups.settings.audit.authority.records.page.size.item.put", "audit.config.groups.settings.audit.authority.records.retention.period.item.put", "audit.config.groups.settings.audit.authority.collection.get"]"""); + private static final Header USER_PERMS_HEADER = new Header(XOkapiHeaders.PERMISSIONS, """ + ["audit.config.groups.settings.collection.get", + "audit.config.groups.settings.audit.users.enabled.item.put", + "audit.config.groups.settings.audit.users.collection.get"]"""); private static final Header USER_HEADER = new Header(XOkapiHeaders.USER_ID, UUID.randomUUID().toString()); private static final Header CONTENT_TYPE_HEADER = new Header("Content-Type", "application/json"); private static final Headers INVENTORY_HEADERS = new Headers(TENANT_HEADER, INVENTORY_PERMS_HEADER, USER_HEADER, CONTENT_TYPE_HEADER); private static final Headers AUTHORITY_HEADERS = new Headers(TENANT_HEADER, AUTHORITY_PERMS_HEADER, USER_HEADER, CONTENT_TYPE_HEADER); + private static final Headers USER_HEADERS = + new Headers(TENANT_HEADER, USER_PERMS_HEADER, USER_HEADER, CONTENT_TYPE_HEADER); private static final String AUDIT_CONFIG_GROUPS_PATH = "/audit/config/groups"; private static final String AUDIT_CONFIG_SETTINGS_PATH = "/audit/config/groups/%s/settings"; private static final String AUDIT_CONFIG_SETTING_ENTRY_PATH = "/audit/config/groups/%s/settings/%s"; @@ -59,7 +66,7 @@ void shouldReturnSettingGroupCollection() { .then().log().all() .statusCode(HttpStatus.HTTP_OK.toInt()) .assertThat() - .body("totalRecords", equalTo(2)) + .body("totalRecords", equalTo(3)) .body("settingGroups[0].id", equalTo(AUTHORITY.getId())); } @@ -145,7 +152,8 @@ private static Stream settingValues() { Arguments.of(INVENTORY.getId(), RETENTION_PERIOD.getValue(), 1, INTEGER, INVENTORY_HEADERS), Arguments.of(AUTHORITY.getId(), RETENTION_PERIOD.getValue(), 3, INTEGER, AUTHORITY_HEADERS), Arguments.of(INVENTORY.getId(), ENABLED.getValue(), false, BOOLEAN, INVENTORY_HEADERS), - Arguments.of(AUTHORITY.getId(), ENABLED.getValue(), false, BOOLEAN, AUTHORITY_HEADERS) + Arguments.of(AUTHORITY.getId(), ENABLED.getValue(), false, BOOLEAN, AUTHORITY_HEADERS), + Arguments.of(USER.getId(), ENABLED.getValue(), true, BOOLEAN, USER_HEADERS) ); } @@ -157,4 +165,31 @@ private JsonObject getSimpleSettingObject(String groupId, String key, Object val .put("type", type.value()); } + @Test + void shouldReturnUserSettingCollection() { + given().headers(USER_HEADERS).get(AUDIT_CONFIG_SETTINGS_PATH.formatted(USER.getId())) + .then().log().all() + .statusCode(HttpStatus.HTTP_OK.toInt()) + .assertThat() + .body("totalRecords", equalTo(1)) + .body("settings[0].key", equalTo(ENABLED.getValue())) + .body("settings[0].value", notNullValue()) + .body("settings[0].type", equalTo("BOOLEAN")) + .body("settings[0].groupId", equalTo(USER.getId())); + } + + @Test + void shouldReturn403OnGetUserSettingCollection_whenNoGroupPermission() { + var permsHeader = new Header(XOkapiHeaders.PERMISSIONS, + "[\"audit.config.groups.settings.audit.circulation.collection.get\"]"); + var headers = new Headers(TENANT_HEADER, USER_HEADER, CONTENT_TYPE_HEADER, permsHeader); + given().headers(headers).get(AUDIT_CONFIG_SETTINGS_PATH.formatted(USER.getId())) + .then().log().all() + .statusCode(HttpStatus.HTTP_FORBIDDEN.toInt()) + .assertThat() + .body("errors[0].message", + containsString("'audit.config.groups.settings.audit.users.collection.get' required")) + .body("errors[0].code", equalTo("unauthorized")); + } + } From 436ee7146b0c49a497a0820f9aee3c1caf86b731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=A4mmer?= Date: Mon, 9 Feb 2026 14:30:24 +0100 Subject: [PATCH 2/4] [MODAUD-296] Remove unnecessary tests --- .../folio/rest/impl/AuditConfigAPITest.java | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java index 51699614..7a3a77bf 100644 --- a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java +++ b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java @@ -165,31 +165,4 @@ private JsonObject getSimpleSettingObject(String groupId, String key, Object val .put("type", type.value()); } - @Test - void shouldReturnUserSettingCollection() { - given().headers(USER_HEADERS).get(AUDIT_CONFIG_SETTINGS_PATH.formatted(USER.getId())) - .then().log().all() - .statusCode(HttpStatus.HTTP_OK.toInt()) - .assertThat() - .body("totalRecords", equalTo(1)) - .body("settings[0].key", equalTo(ENABLED.getValue())) - .body("settings[0].value", notNullValue()) - .body("settings[0].type", equalTo("BOOLEAN")) - .body("settings[0].groupId", equalTo(USER.getId())); - } - - @Test - void shouldReturn403OnGetUserSettingCollection_whenNoGroupPermission() { - var permsHeader = new Header(XOkapiHeaders.PERMISSIONS, - "[\"audit.config.groups.settings.audit.circulation.collection.get\"]"); - var headers = new Headers(TENANT_HEADER, USER_HEADER, CONTENT_TYPE_HEADER, permsHeader); - given().headers(headers).get(AUDIT_CONFIG_SETTINGS_PATH.formatted(USER.getId())) - .then().log().all() - .statusCode(HttpStatus.HTTP_FORBIDDEN.toInt()) - .assertThat() - .body("errors[0].message", - containsString("'audit.config.groups.settings.audit.users.collection.get' required")) - .body("errors[0].code", equalTo("unauthorized")); - } - } From 29c9f24d517b0f68cff00de5ba33bfeb76e0e4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=A4mmer?= Date: Mon, 9 Feb 2026 14:55:35 +0100 Subject: [PATCH 3/4] [MODAUD-296] Use singular user instead of plural users --- descriptors/ModuleDescriptor-template.json | 8 ++++---- .../org/folio/services/configuration/SettingGroup.java | 2 +- .../db_scripts/config/populate_setting_group_table.sql | 2 +- .../db_scripts/config/populate_setting_table.sql | 4 ++-- .../test/java/org/folio/rest/impl/AuditConfigAPITest.java | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 1a2d3530..c37689c4 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -481,12 +481,12 @@ "description": "Enable/disable audit authority records" }, { - "permissionName": "audit.config.groups.settings.audit.users.collection.get", - "displayName": "Audit Configuration - get settings for audit users group", - "description": "Get settings for audit users group" + "permissionName": "audit.config.groups.settings.audit.user.collection.get", + "displayName": "Audit Configuration - get settings for audit user group", + "description": "Get settings for audit user group" }, { - "permissionName": "audit.config.groups.settings.audit.users.enabled.item.put", + "permissionName": "audit.config.groups.settings.audit.user.enabled.item.put", "displayName": "Audit Configuration - enable/disable audit user records", "description": "Enable/disable audit user records" }, diff --git a/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java b/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java index e285056d..81941a75 100644 --- a/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java +++ b/mod-audit-server/src/main/java/org/folio/services/configuration/SettingGroup.java @@ -7,7 +7,7 @@ public enum SettingGroup { AUTHORITY("audit.authority"), INVENTORY("audit.inventory"), - USER("audit.users"); + USER("audit.user"); private final String id; diff --git a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql index a3a5b3ba..300839c3 100644 --- a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql +++ b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_group_table.sql @@ -5,7 +5,7 @@ VALUES ('audit.inventory', ('audit.authority', 'Authority Audit Configuration', 'Group of configurations for audit of authority records'), - ('audit.users', + ('audit.user', 'User Audit Configuration', 'Group of configurations for audit of user records') ON CONFLICT (id) DO NOTHING; diff --git a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql index 6e172baa..89c2643b 100644 --- a/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql +++ b/mod-audit-server/src/main/resources/templates/db_scripts/config/populate_setting_table.sql @@ -59,12 +59,12 @@ VALUES ('audit.inventory.records.page.size', '00000000-0000-0000-0000-000000000000', now(), '00000000-0000-0000-0000-000000000000'), - ('audit.users.enabled', + ('audit.user.enabled', 'enabled', to_jsonb(false::boolean), 'BOOLEAN', 'Defines if the user audit is enabled', - 'audit.users', + 'audit.user', now(), '00000000-0000-0000-0000-000000000000', now(), diff --git a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java index 7a3a77bf..06288a78 100644 --- a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java +++ b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditConfigAPITest.java @@ -46,8 +46,8 @@ public class AuditConfigAPITest extends ApiTestBase { "audit.config.groups.settings.audit.authority.collection.get"]"""); private static final Header USER_PERMS_HEADER = new Header(XOkapiHeaders.PERMISSIONS, """ ["audit.config.groups.settings.collection.get", - "audit.config.groups.settings.audit.users.enabled.item.put", - "audit.config.groups.settings.audit.users.collection.get"]"""); + "audit.config.groups.settings.audit.user.enabled.item.put", + "audit.config.groups.settings.audit.user.collection.get"]"""); private static final Header USER_HEADER = new Header(XOkapiHeaders.USER_ID, UUID.randomUUID().toString()); private static final Header CONTENT_TYPE_HEADER = new Header("Content-Type", "application/json"); private static final Headers INVENTORY_HEADERS = From 27fa463ecb5898608474e3bdb15f5dc9fc92f7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=A4mmer?= Date: Tue, 10 Feb 2026 16:11:01 +0100 Subject: [PATCH 4/4] [MODAUD-296] Add entry to NEWS.md --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 6d6512cb..851c5bc3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,7 @@ * [MODAUD-265](https://folio-org.atlassian.net/browse/MODAUD-265) - Bump Inventory Item Storage API version from 10.1 to 11.0 * [MODAUD-284](https://folio-org.atlassian.net/browse/MODAUD-284) - Prepare sub-partitions on tenant init * [MODAUD-288](https://folio-org.atlassian.net/browse/MODAUD-288) - assertj: Upgrade from 3.27.3 to 3.27.7, change scope from compile to test +* [MODAUD-296](https://folio-org.atlassian.net/browse/MODAUD-296) - Implement User Audit Enabled Setting ## 2.11.1 2025-04-15 * [MODAUD-250](https://folio-org.atlassian.net/browse/MODAUD-250) - Version history of "MARC" records is not tracked