From 7b17e191f7355879d3d7f6e3c343c2d03af199fa Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 29 Jan 2026 10:30:52 +0200 Subject: [PATCH 1/8] UI: Fixed import --- ui-ngx/src/app/shared/models/alarm.models.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index 162b898513..41e8940d55 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -28,7 +28,6 @@ import { UserId } from '@shared/models/id/user-id'; import { AlarmFilter } from '@shared/models/query/query.models'; import { HasTenantId } from '@shared/models/entity.models'; import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; -import { defaults } from 'lodash'; export enum AlarmsMode { ALL, From ec443f116c97e2f9f6ad6233306249bdb9d24e9f Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Mon, 31 Mar 2025 15:16:24 +0200 Subject: [PATCH 2/8] Audit logging for TenantProfile Signed-off-by: Oleksandra Matviienko --- .../server/controller/TenantProfileController.java | 4 ++-- .../tenant/profile/DefaultTbTenantProfileService.java | 11 +++++++++-- .../tenant/profile/TbTenantProfileService.java | 5 +++-- .../server/controller/AbstractWebTest.java | 3 ++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index c0f7b9eb40..2dc6d6310c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -191,7 +191,7 @@ public class TenantProfileController extends BaseController { oldProfile = checkTenantProfileId(tenantProfile.getId(), Operation.WRITE); } - return tbTenantProfileService.save(getTenantId(), tenantProfile, oldProfile); + return tbTenantProfileService.save(getTenantId(), tenantProfile, oldProfile, getCurrentUser()); } @ApiOperation(value = "Delete Tenant Profile (deleteTenantProfile)", @@ -204,7 +204,7 @@ public class TenantProfileController extends BaseController { checkParameter("tenantProfileId", strTenantProfileId); TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE); - tbTenantProfileService.delete(getTenantId(), profile); + tbTenantProfileService.delete(getTenantId(), profile, getCurrentUser()); } @ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java index fce44972d8..8a1553b267 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java @@ -19,8 +19,10 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; @@ -41,18 +43,23 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple private final TbTenantProfileCache tenantProfileCache; @Override - public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException { + public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException { + ActionType actionType = tenantProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); tenantProfileCache.put(savedTenantProfile); List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); + logEntityActionService.logEntityAction(tenantId, savedTenantProfile.getId(), savedTenantProfile, null, + actionType, user); return savedTenantProfile; } @Override - public void delete(TenantId tenantId, TenantProfile tenantProfile) throws ThingsboardException { + public void delete(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException { + ActionType actionType = ActionType.DELETED; tenantProfileService.deleteTenantProfile(tenantId, tenantProfile.getId()); + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java index 3ca8f0570b..564ced935b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java @@ -18,10 +18,11 @@ package org.thingsboard.server.service.entitiy.tenant.profile; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.User; public interface TbTenantProfileService { - TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException; + TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException; - void delete(TenantId tenantId, TenantProfile tenantProfile) throws ThingsboardException; + void delete(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException; } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 6fbe9d9864..52423619d8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -1265,10 +1265,11 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } protected void updateDefaultTenantProfile(Consumer updater) throws ThingsboardException { + User user = Mockito.mock(User.class); TenantProfile oldTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile); updater.accept(tenantProfile); - tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile); + tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile, user); } protected OAuth2Client createOauth2Client(TenantId tenantId, String title) { From 164ff0d467f82e2aef24069dd3a85d2e7e7d016d Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Wed, 20 Aug 2025 17:11:42 +0200 Subject: [PATCH 3/8] Refactored audit logs for save/delete tenant profile operations; moved setDefaultTenantProfile to the service layer; added Awaitility-based audit log checks in controller tests; allowed SYS_ADMIN access to audit endpoints; made BuildProperties optional with version fallback; used tenantAdminUser in updateDefaultTenantProfile; updated logging config for audit debugging. Signed-off-by: Oleksandra_Matviienko --- .../server/controller/AuditLogController.java | 4 +- .../controller/TenantProfileController.java | 2 +- .../DefaultTbTenantProfileService.java | 67 ++++++++++++++++--- .../profile/TbTenantProfileService.java | 2 + .../controller/AbstractNotifyEntityTest.java | 5 ++ .../server/controller/AbstractWebTest.java | 3 +- .../TenantProfileControllerTest.java | 32 ++++++++- .../src/test/resources/logback-test.xml | 1 + .../server/dao/audit/AuditLogServiceImpl.java | 2 +- 9 files changed, 100 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index e22c0aa156..5d34959530 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -139,7 +139,7 @@ public class AuditLogController extends BaseController { "Basically, this API call is used to get the full lifecycle of some specific entity. " + "For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) - @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody public PageData getAuditLogsByEntityId( @@ -174,7 +174,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get all audit logs (getAuditLogs)", notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) - @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody public PageData getAuditLogs( diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index 2dc6d6310c..18bc488394 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -217,7 +217,7 @@ public class TenantProfileController extends BaseController { checkParameter("tenantProfileId", strTenantProfileId); TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); - tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); + tenantProfile = tbTenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfile, getCurrentUser()); return tenantProfile; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java index 8a1553b267..c2155678bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java @@ -18,11 +18,14 @@ package org.thingsboard.server.service.entitiy.tenant.profile; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; @@ -43,23 +46,67 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple private final TbTenantProfileCache tenantProfileCache; @Override - public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException { + public TenantProfile + save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException { ActionType actionType = tenantProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED; - TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); - tenantProfileCache.put(savedTenantProfile); + try { + TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); + tenantProfileCache.put(savedTenantProfile); - List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); - tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); - logEntityActionService.logEntityAction(tenantId, savedTenantProfile.getId(), savedTenantProfile, null, - actionType, user); + List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); + tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); + logEntityActionService.logEntityAction(tenantId, savedTenantProfile.getId(), savedTenantProfile, null, + actionType, user); + + return savedTenantProfile; + } catch (ThingsboardException e) { + log.error("Failed to save tenant profile because ThingsboardException [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); + throw e; + } catch (DataValidationException e) { + log.error("Failed to save tenant profile because data validation [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); + throw new ThingsboardException(e.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } catch (Exception e) { + log.error("Failed to save tenant profile because Exception [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); + throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL); + } - return savedTenantProfile; } @Override public void delete(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException { ActionType actionType = ActionType.DELETED; - tenantProfileService.deleteTenantProfile(tenantId, tenantProfile.getId()); - logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user); + try { + tenantProfileService.deleteTenantProfile(tenantId, tenantProfile.getId()); + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user); + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user); + throw e; + } + } + + @Override + public TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException { + ActionType actionType = ActionType.UPDATED; + try { + boolean changed = tenantProfileService.setDefaultTenantProfile(tenantId, tenantProfile.getId()); + TenantProfile result = tenantProfileService.findTenantProfileById(tenantId, tenantProfile.getId()); + if (changed && result != null) { + // Update application-level cache + tenantProfileCache.put(result); + } + logEntityActionService.logEntityAction(tenantId, result != null ? result.getId() : tenantProfile.getId(), result, null, actionType, user); + return result != null ? result : tenantProfile; + } catch (DataValidationException e) { + log.error("Failed to set default tenant profile due to data validation [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); + throw new ThingsboardException(e.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } catch (Exception e) { + log.error("Failed to set default tenant profile [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); + throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL); + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java index 564ced935b..66ddbbca2a 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java @@ -25,4 +25,6 @@ public interface TbTenantProfileService { TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException; void delete(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException; + + TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException; } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index fcbd60eb4d..62a266f26a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -18,6 +18,8 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; import org.mockito.ArgumentMatcher; import org.mockito.Mockito; +import org.springframework.boot.info.BuildProperties; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EdgeUtils; @@ -59,6 +61,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean protected AuditLogService auditLogService; + @MockBean + BuildProperties buildProperties; + protected final String msgErrorPermission = "You don't have permission to perform this operation!"; protected final String msgErrorShouldBeSpecified = "should be specified"; protected final String msgErrorNotFound = "Requested item wasn't found!"; diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 52423619d8..6ed5f85147 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -1265,11 +1265,10 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } protected void updateDefaultTenantProfile(Consumer updater) throws ThingsboardException { - User user = Mockito.mock(User.class); TenantProfile oldTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile); updater.accept(tenantProfile); - tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile, user); + tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile, tenantAdminUser); } protected OAuth2Client createOauth2Client(TenantId tenantId, String title) { diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java index 42a27eb6d8..feca8ed97b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java @@ -25,9 +25,12 @@ import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; @@ -39,6 +42,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfi import org.thingsboard.server.common.data.validation.RateLimit; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.TbQueueCallback; +import org.awaitility.Awaitility; import java.lang.reflect.Field; import java.util.ArrayList; @@ -47,6 +51,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -81,12 +86,17 @@ public class TenantProfileControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1); + awaitAuditLog("Wait for async audit log to be persisted (ADDED expected)", savedTenantProfile.getId(), ActionType.ADDED); + savedTenantProfile.setName("New tenant profile"); doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class); TenantProfile foundTenantProfile = doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString(), TenantProfile.class); Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.UPDATED, 1); + + awaitAuditLog("Wait for async audit log to be persisted (UPDATED expected)", savedTenantProfile.getId(), ActionType.UPDATED); + } @Test @@ -180,7 +190,8 @@ public class TenantProfileControllerTest extends AbstractControllerTest { Assert.assertNotNull(foundDefaultTenantProfile); Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfile.getName()); Assert.assertEquals(savedTenantProfile.getId(), foundDefaultTenantProfile.getId()); - } + + awaitAuditLog("Wait for async audit log to be persisted (UPDATED expected for NEW DEFAULT Tenant Profile)", savedTenantProfile.getId(), ActionType.UPDATED); } @Test public void testSaveTenantProfileWithEmptyName() throws Exception { @@ -245,6 +256,8 @@ public class TenantProfileControllerTest extends AbstractControllerTest { doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) .andExpect(status().isOk()); + awaitAuditLog("Wait for async audit log to be persisted for deleted tenant profile", savedTenantProfile.getId(), ActionType.DELETED); + testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.DELETED, 1); doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) @@ -393,6 +406,22 @@ public class TenantProfileControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventNeverTenantProfile(); } + private void awaitAuditLog(String awaitMessage, TenantProfileId tenantProfileId, ActionType expectedAction) throws Exception { + Awaitility.await(awaitMessage) + .atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> + doGetTypedWithTimePageLink( + "/api/audit/logs/entity/TENANT_PROFILE/" + tenantProfileId.getId() + "?", + new TypeReference>() { + }, + new TimePageLink(5)) + .getData() + .stream() + .anyMatch(log -> log.getActionType() == expectedAction) + ); + + } + private TenantProfile createTenantProfile(String name) { TenantProfile tenantProfile = new TenantProfile(); tenantProfile.setName(name); @@ -429,7 +458,6 @@ public class TenantProfileControllerTest extends AbstractControllerTest { tenantProfile.setProfileData(profileData); } - private void testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(TenantProfile tenantProfile, ComponentLifecycleEvent event, int cntTime) { ArgumentMatcher matcherTenantProfile = cntTime == 1 ? argument -> argument.equals(tenantProfile) : argument -> argument.getClass().equals(TenantProfile.class); diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 6faaf97536..b5a1ac86ae 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -16,6 +16,7 @@ + diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 27a52e1cc7..90fe102fce 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -119,7 +119,7 @@ public class AuditLogServiceImpl implements AuditLogService { public ListenableFuture logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { - if (canLog(entityId.getEntityType(), actionType)) { + if (canLog(entityId.getEntityType(), actionType) || tenantId.isSysTenantId()) { JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo); ActionStatus actionStatus = ActionStatus.SUCCESS; String failureDetails = ""; From 90404d80fd89bffc6a95bdd1c2a7f803ee666cae Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Mon, 26 Jan 2026 22:57:03 +0100 Subject: [PATCH 4/8] sysadmin audit log refactored after review Signed-off-by: Oleksandra_Matviienko --- .../server/controller/AuditLogController.java | 5 +++-- .../server/controller/BaseController.java | 6 ++---- .../profile/DefaultTbTenantProfileService.java | 15 +++++---------- .../server/controller/AbstractWebTest.java | 1 + .../controller/TenantProfileControllerTest.java | 7 ++++--- .../server/dao/tenant/TenantProfileService.java | 2 +- .../server/dao/audit/AuditLogServiceImpl.java | 2 +- .../dao/tenant/TenantProfileServiceImpl.java | 12 ++++-------- .../dao/service/TenantProfileServiceTest.java | 8 ++++---- 9 files changed, 25 insertions(+), 33 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index 5d34959530..8c0d0d2243 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -50,6 +50,7 @@ import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PA import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARAM_DESCRIPTION; @@ -138,7 +139,7 @@ public class AuditLogController extends BaseController { notes = "Returns a page of audit logs related to the actions on the targeted entity. " + "Basically, this API call is used to get the full lifecycle of some specific entity. " + "For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) + PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -173,7 +174,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get all audit logs (getAuditLogs)", notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) + PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index bd2a160d5b..48e0f11552 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -881,10 +881,8 @@ public abstract class BaseController { protected > void logEntityAction(SecurityUser user, EntityType entityType, E entity, E savedEntity, ActionType actionType, Exception e) { EntityId entityId = savedEntity != null ? savedEntity.getId() : emptyId(entityType); - if (!user.isSystemAdmin()) { - entityActionService.logEntityAction(user, entityId, savedEntity != null ? savedEntity : entity, - user.getCustomerId(), actionType, e); - } + entityActionService.logEntityAction(user, entityId, savedEntity != null ? savedEntity : entity, + user.getCustomerId(), actionType, e); } protected > E doSaveAndLog(EntityType entityType, E entity, BiFunction savingFunction) throws Exception { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java index c2155678bd..cecc365c7c 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java @@ -52,11 +52,11 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple try { TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); tenantProfileCache.put(savedTenantProfile); + logEntityActionService.logEntityAction(tenantId, savedTenantProfile.getId(), savedTenantProfile, null, + actionType, user); List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); - logEntityActionService.logEntityAction(tenantId, savedTenantProfile.getId(), savedTenantProfile, null, - actionType, user); return savedTenantProfile; } catch (ThingsboardException e) { @@ -91,14 +91,9 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple public TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException { ActionType actionType = ActionType.UPDATED; try { - boolean changed = tenantProfileService.setDefaultTenantProfile(tenantId, tenantProfile.getId()); - TenantProfile result = tenantProfileService.findTenantProfileById(tenantId, tenantProfile.getId()); - if (changed && result != null) { - // Update application-level cache - tenantProfileCache.put(result); - } - logEntityActionService.logEntityAction(tenantId, result != null ? result.getId() : tenantProfile.getId(), result, null, actionType, user); - return result != null ? result : tenantProfile; + TenantProfile savedTenantProfile = tenantProfileService.setDefaultTenantProfile(tenantId, tenantProfile.getId()); + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), savedTenantProfile, null, actionType, user); + return savedTenantProfile; } catch (DataValidationException e) { log.error("Failed to set default tenant profile due to data validation [{}]", tenantProfile, e); logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 6ed5f85147..71cbed6d10 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -1268,6 +1268,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { TenantProfile oldTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile); updater.accept(tenantProfile); + // user should be sysadmin as this operation allowed only for sysadmins. But for the simplification of the test - already existed variable provided. This affects only an audit log content tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile, tenantAdminUser); } diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java index feca8ed97b..2454dae974 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.awaitility.Awaitility; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatcher; @@ -25,12 +26,12 @@ import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; @@ -42,7 +43,6 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfi import org.thingsboard.server.common.data.validation.RateLimit; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.TbQueueCallback; -import org.awaitility.Awaitility; import java.lang.reflect.Field; import java.util.ArrayList; @@ -191,7 +191,8 @@ public class TenantProfileControllerTest extends AbstractControllerTest { Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfile.getName()); Assert.assertEquals(savedTenantProfile.getId(), foundDefaultTenantProfile.getId()); - awaitAuditLog("Wait for async audit log to be persisted (UPDATED expected for NEW DEFAULT Tenant Profile)", savedTenantProfile.getId(), ActionType.UPDATED); } + awaitAuditLog("Wait for async audit log to be persisted (UPDATED expected for NEW DEFAULT Tenant Profile)", savedTenantProfile.getId(), ActionType.UPDATED); + } @Test public void testSaveTenantProfileWithEmptyName() throws Exception { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java index ccac3cc5c1..f8f308c8f4 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java @@ -46,7 +46,7 @@ public interface TenantProfileService extends EntityDaoService { EntityInfo findDefaultTenantProfileInfo(TenantId tenantId); - boolean setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId); + TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId); void deleteTenantProfiles(TenantId tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 90fe102fce..a70331da0c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -119,7 +119,7 @@ public class AuditLogServiceImpl implements AuditLogService { public ListenableFuture logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { - if (canLog(entityId.getEntityType(), actionType) || tenantId.isSysTenantId()) { + if (canLog(entityId.getEntityType(), actionType) || (tenantId != null && tenantId.isSysTenantId())) { JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo); ActionStatus actionStatus = ActionStatus.SUCCESS; String failureDetails = ""; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 7be47f4a0e..af62feb226 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -190,7 +190,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id); @@ -198,22 +198,18 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService Date: Thu, 14 Aug 2025 19:39:48 +0200 Subject: [PATCH 5/8] Audit log UI for sysadmin --- ui-ngx/src/app/core/services/menu.models.ts | 3 ++- .../modules/home/pages/audit-log/audit-log-routing.module.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index c2d4fc9e21..ab2160ec0f 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -816,7 +816,8 @@ const defaultUserMenuMap = new Map([ {id: MenuId.domains}, {id: MenuId.clients} ] - } + }, + {id: MenuId.audit_log} ] } ] diff --git a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts index 2a4cd70c90..b9943eb41f 100644 --- a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts @@ -25,7 +25,7 @@ export const auditLogsRoutes: Routes = [ path: 'auditLogs', component: AuditLogTableComponent, data: { - auth: [Authority.TENANT_ADMIN], + auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], title: 'audit-log.audit-logs', breadcrumb: { menuId: MenuId.audit_log From a8de7ec7d5afd4086a0503a5cabd3034d4d8499d Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 14 Aug 2025 20:07:12 +0200 Subject: [PATCH 6/8] Audit log UI tab added for sysadmin for TenantProfile card --- .../pages/tenant-profile/tenant-profile-tabs.component.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html index dcad41d86a..b24e5e247a 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html @@ -31,4 +31,8 @@ [entityName]="entity.name"> + + + } From 84752472b73f7063906becc9b3846f69a8f1e37a Mon Sep 17 00:00:00 2001 From: Oleksandra_Matviienko Date: Thu, 29 Jan 2026 11:04:07 +0100 Subject: [PATCH 7/8] Added @NotNull for null-safety for audit/entity services. Added getOrEmptyId method to validate if entityId is zero. Changed logs to debug in DefaultTbTenantProfileService. Added Throwable cause to Exceptions in catch blocks in DefaultTbTenantProfileService. Signed-off-by: Oleksandra_Matviienko --- .../service/action/EntityActionService.java | 3 +- .../entitiy/AbstractTbEntityService.java | 4 ++ .../DefaultTbLogEntityActionService.java | 3 +- .../entitiy/TbLogEntityActionService.java | 3 +- .../DefaultTbTenantProfileService.java | 38 ++++++++++--------- .../profile/TbTenantProfileService.java | 5 ++- .../src/test/resources/logback-test.xml | 1 - .../server/dao/audit/AuditLogService.java | 3 +- .../server/dao/audit/AuditLogServiceImpl.java | 3 +- 9 files changed, 37 insertions(+), 26 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index 774396a704..3b72ac662f 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.action; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -235,7 +236,7 @@ public class EntityActionService { } } - public void logEntityAction(User user, I entityId, E entity, CustomerId customerId, + public void logEntityAction(User user, @NotNull I entityId, E entity, CustomerId customerId, ActionType actionType, Exception e, Object... additionalInfo) { if (customerId == null || customerId.isNullUid()) { customerId = user.getCustomerId(); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index 279ed918c8..efa000e521 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -97,6 +97,10 @@ public abstract class AbstractTbEntityService { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } + protected I getOrEmptyId(I entityId, EntityType entityType) { + return entityId == null ? emptyId(entityType) : entityId; + } + protected ListenableFuture autoCommit(User user, EntityId entityId) { if (vcService != null) { return vcService.autoCommit(user, entityId); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbLogEntityActionService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbLogEntityActionService.java index 05ec142e9d..685e447887 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbLogEntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbLogEntityActionService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -60,7 +61,7 @@ public class DefaultTbLogEntityActionService implements TbLogEntityActionService } @Override - public void logEntityAction(TenantId tenantId, I entityId, E entity, + public void logEntityAction(TenantId tenantId, @NotNull I entityId, E entity, CustomerId customerId, ActionType actionType, User user, Exception e, Object... additionalInfo) { if (user != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbLogEntityActionService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbLogEntityActionService.java index 712bca5b49..f574d4df23 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbLogEntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbLogEntityActionService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy; +import jakarta.validation.constraints.NotNull; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -37,7 +38,7 @@ public interface TbLogEntityActionService { void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, User user, Object... additionalInfo); - void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, + void logEntityAction(TenantId tenantId, @NotNull I entityId, E entity, CustomerId customerId, ActionType actionType, User user, Exception e, Object... additionalInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java index cecc365c7c..a178832075 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.service.entitiy.tenant.profile; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -35,6 +35,8 @@ import org.thingsboard.server.service.entitiy.queue.TbQueueService; import java.util.List; +import static org.thingsboard.server.common.data.EntityType.TENANT_PROFILE; + @Slf4j @Service @TbCoreComponent @@ -60,48 +62,48 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple return savedTenantProfile; } catch (ThingsboardException e) { - log.error("Failed to save tenant profile because ThingsboardException [{}]", tenantProfile, e); - logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); + log.debug("Failed to save tenant profile because ThingsboardException [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, getOrEmptyId(tenantProfile.getId(), TENANT_PROFILE), tenantProfile, actionType, user, e); throw e; } catch (DataValidationException e) { - log.error("Failed to save tenant profile because data validation [{}]", tenantProfile, e); - logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); - throw new ThingsboardException(e.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + log.debug("Failed to save tenant profile because data validation [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, getOrEmptyId(tenantProfile.getId(), TENANT_PROFILE), tenantProfile, actionType, user, e); + throw new ThingsboardException(e.getMessage(), e, ThingsboardErrorCode.BAD_REQUEST_PARAMS); } catch (Exception e) { - log.error("Failed to save tenant profile because Exception [{}]", tenantProfile, e); - logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); - throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL); + log.debug("Failed to save tenant profile because Exception [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, getOrEmptyId(tenantProfile.getId(), TENANT_PROFILE), tenantProfile, actionType, user, e); + throw new ThingsboardException(e.getMessage(), e, ThingsboardErrorCode.GENERAL); } } @Override - public void delete(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException { + public void delete(TenantId tenantId, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException { ActionType actionType = ActionType.DELETED; try { tenantProfileService.deleteTenantProfile(tenantId, tenantProfile.getId()); logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user); } catch (Exception e) { - logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user); + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, null, actionType, user, e); throw e; } } @Override - public TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException { + public TenantProfile setDefaultTenantProfile(TenantId tenantId, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException { ActionType actionType = ActionType.UPDATED; try { TenantProfile savedTenantProfile = tenantProfileService.setDefaultTenantProfile(tenantId, tenantProfile.getId()); logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), savedTenantProfile, null, actionType, user); return savedTenantProfile; } catch (DataValidationException e) { - log.error("Failed to set default tenant profile due to data validation [{}]", tenantProfile, e); - logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); - throw new ThingsboardException(e.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + log.debug("Failed to set default tenant profile due to data validation [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, actionType, user, e); + throw new ThingsboardException(e.getMessage(), e, ThingsboardErrorCode.BAD_REQUEST_PARAMS); } catch (Exception e) { - log.error("Failed to set default tenant profile [{}]", tenantProfile, e); - logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.TENANT_PROFILE), tenantProfile, actionType, user, e); - throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL); + log.debug("Failed to set default tenant profile [{}]", tenantProfile, e); + logEntityActionService.logEntityAction(tenantId, tenantProfile.getId(), tenantProfile, actionType, user, e); + throw new ThingsboardException(e.getMessage(), e, ThingsboardErrorCode.GENERAL); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java index 66ddbbca2a..d15e9c84c8 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.tenant.profile; +import jakarta.validation.constraints.NotNull; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; @@ -24,7 +25,7 @@ public interface TbTenantProfileService { TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException; - void delete(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException; + void delete(TenantId tenantId, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException; - TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfile tenantProfile, User user) throws ThingsboardException; + TenantProfile setDefaultTenantProfile(TenantId tenantId, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException; } diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index b5a1ac86ae..6faaf97536 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -16,7 +16,6 @@ - diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java index df5833576a..07ea2243dc 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.audit; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.validation.constraints.NotNull; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; @@ -43,7 +44,7 @@ public interface AuditLogService { CustomerId customerId, UserId userId, String userName, - I entityId, + @NotNull I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo); diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index a70331da0c..8aef814657 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -117,7 +118,7 @@ public class AuditLogServiceImpl implements AuditLogService { @Override public ListenableFuture - logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, + logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, @NotNull I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { if (canLog(entityId.getEntityType(), actionType) || (tenantId != null && tenantId.isSysTenantId())) { JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo); From bb3237bf3f7e91f7fded72e74b7aeff0da1212e8 Mon Sep 17 00:00:00 2001 From: Oleksandra_Matviienko Date: Thu, 29 Jan 2026 15:29:38 +0100 Subject: [PATCH 8/8] fixed import DataValidationException Signed-off-by: Oleksandra_Matviienko --- .../entitiy/tenant/profile/DefaultTbTenantProfileService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java index a178832075..25dadbe316 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java @@ -25,10 +25,10 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.queue.TbQueueService;