Browse Source

Merge remote-tracking branch 'origin/rc' into master-rc

pull/14974/head
Viacheslav Klimov 6 months ago
parent
commit
c59cd4a791
  1. 9
      application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
  2. 6
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 6
      application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
  4. 3
      application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
  5. 4
      application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java
  6. 3
      application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbLogEntityActionService.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/entitiy/TbLogEntityActionService.java
  8. 67
      application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java
  9. 8
      application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java
  10. 5
      application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
  11. 3
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  12. 94
      application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest.java
  13. 31
      application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java
  14. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
  15. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java
  16. 2
      common/edge-api/src/main/proto/edge.proto
  17. 5
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  18. 12
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java
  19. 8
      dao/src/test/java/org/thingsboard/server/dao/service/TenantProfileServiceTest.java
  20. 3
      ui-ngx/src/app/core/services/menu.models.ts
  21. 2
      ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts
  22. 4
      ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html

9
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_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_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.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.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARAM_DESCRIPTION;
@ -138,8 +139,8 @@ public class AuditLogController extends BaseController {
notes = "Returns a page of audit logs related to the actions on the targeted entity. " + 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. " + "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. " + "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("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<AuditLog> getAuditLogsByEntityId( public PageData<AuditLog> getAuditLogsByEntityId(
@ -173,8 +174,8 @@ public class AuditLogController extends BaseController {
@ApiOperation(value = "Get all audit logs (getAuditLogs)", @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. " + 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("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<AuditLog> getAuditLogs( public PageData<AuditLog> getAuditLogs(

6
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -881,10 +881,8 @@ public abstract class BaseController {
protected <E extends HasName & HasId<? extends EntityId>> void logEntityAction(SecurityUser user, EntityType entityType, E entity, E savedEntity, ActionType actionType, Exception e) { protected <E extends HasName & HasId<? extends EntityId>> void logEntityAction(SecurityUser user, EntityType entityType, E entity, E savedEntity, ActionType actionType, Exception e) {
EntityId entityId = savedEntity != null ? savedEntity.getId() : emptyId(entityType); EntityId entityId = savedEntity != null ? savedEntity.getId() : emptyId(entityType);
if (!user.isSystemAdmin()) { entityActionService.logEntityAction(user, entityId, savedEntity != null ? savedEntity : entity,
entityActionService.logEntityAction(user, entityId, savedEntity != null ? savedEntity : entity, user.getCustomerId(), actionType, e);
user.getCustomerId(), actionType, e);
}
} }
protected <E extends HasName & HasId<? extends EntityId>> E doSaveAndLog(EntityType entityType, E entity, BiFunction<TenantId, E, E> savingFunction) throws Exception { protected <E extends HasName & HasId<? extends EntityId>> E doSaveAndLog(EntityType entityType, E entity, BiFunction<TenantId, E, E> savingFunction) throws Exception {

6
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); 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)", @ApiOperation(value = "Delete Tenant Profile (deleteTenantProfile)",
@ -204,7 +204,7 @@ public class TenantProfileController extends BaseController {
checkParameter("tenantProfileId", strTenantProfileId); checkParameter("tenantProfileId", strTenantProfileId);
TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE); TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE);
tbTenantProfileService.delete(getTenantId(), profile); tbTenantProfileService.delete(getTenantId(), profile, getCurrentUser());
} }
@ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)", @ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)",
@ -217,7 +217,7 @@ public class TenantProfileController extends BaseController {
checkParameter("tenantProfileId", strTenantProfileId); checkParameter("tenantProfileId", strTenantProfileId);
TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE);
tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); tenantProfile = tbTenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfile, getCurrentUser());
return tenantProfile; return tenantProfile;
} }

3
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.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -235,7 +236,7 @@ public class EntityActionService {
} }
} }
public <E extends HasName, I extends EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId, public <E extends HasName, I extends EntityId> void logEntityAction(User user, @NotNull I entityId, E entity, CustomerId customerId,
ActionType actionType, Exception e, Object... additionalInfo) { ActionType actionType, Exception e, Object... additionalInfo) {
if (customerId == null || customerId.isNullUid()) { if (customerId == null || customerId.isNullUid()) {
customerId = user.getCustomerId(); customerId = user.getCustomerId();

4
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); return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
} }
protected <I extends EntityId> I getOrEmptyId(I entityId, EntityType entityType) {
return entityId == null ? emptyId(entityType) : entityId;
}
protected ListenableFuture<UUID> autoCommit(User user, EntityId entityId) { protected ListenableFuture<UUID> autoCommit(User user, EntityId entityId) {
if (vcService != null) { if (vcService != null) {
return vcService.autoCommit(user, entityId); return vcService.autoCommit(user, entityId);

3
application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbLogEntityActionService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.service.entitiy; package org.thingsboard.server.service.entitiy;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -60,7 +61,7 @@ public class DefaultTbLogEntityActionService implements TbLogEntityActionService
} }
@Override @Override
public <E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, E entity, public <E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, @NotNull I entityId, E entity,
CustomerId customerId, ActionType actionType, CustomerId customerId, ActionType actionType,
User user, Exception e, Object... additionalInfo) { User user, Exception e, Object... additionalInfo) {
if (user != null) { if (user != null) {

3
application/src/main/java/org/thingsboard/server/service/entitiy/TbLogEntityActionService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.service.entitiy; package org.thingsboard.server.service.entitiy;
import jakarta.validation.constraints.NotNull;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
@ -37,7 +38,7 @@ public interface TbLogEntityActionService {
<E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, <E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId,
ActionType actionType, User user, Object... additionalInfo); ActionType actionType, User user, Object... additionalInfo);
<E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, <E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, @NotNull I entityId, E entity, CustomerId customerId,
ActionType actionType, User user, Exception e, ActionType actionType, User user, Exception e,
Object... additionalInfo); Object... additionalInfo);

67
application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java

@ -15,21 +15,28 @@
*/ */
package org.thingsboard.server.service.entitiy.tenant.profile; package org.thingsboard.server.service.entitiy.tenant.profile;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.TenantProfile; 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.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; 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.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.exception.DataValidationException;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.entitiy.queue.TbQueueService; import org.thingsboard.server.service.entitiy.queue.TbQueueService;
import java.util.List; import java.util.List;
import static org.thingsboard.server.common.data.EntityType.TENANT_PROFILE;
@Slf4j @Slf4j
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -41,18 +48,62 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple
private final TbTenantProfileCache tenantProfileCache; private final TbTenantProfileCache tenantProfileCache;
@Override @Override
public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException { public TenantProfile
TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile, User user) throws ThingsboardException {
tenantProfileCache.put(savedTenantProfile); ActionType actionType = tenantProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
try {
TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile));
tenantProfileCache.put(savedTenantProfile);
logEntityActionService.logEntityAction(tenantId, savedTenantProfile.getId(), savedTenantProfile, null,
actionType, user);
List<TenantId> tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId());
tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile);
List<TenantId> tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); return savedTenantProfile;
tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); } catch (ThingsboardException 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.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.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);
}
return savedTenantProfile;
} }
@Override @Override
public void delete(TenantId tenantId, TenantProfile tenantProfile) throws ThingsboardException { public void delete(TenantId tenantId, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException {
tenantProfileService.deleteTenantProfile(tenantId, tenantProfile.getId()); 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, e);
throw e;
}
}
@Override
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.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.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);
}
} }
} }

8
application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java

@ -15,13 +15,17 @@
*/ */
package org.thingsboard.server.service.entitiy.tenant.profile; 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.TenantProfile;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.User;
public interface TbTenantProfileService { 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, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException;
TenantProfile setDefaultTenantProfile(TenantId tenantId, @NotNull TenantProfile tenantProfile, User user) throws ThingsboardException;
} }

5
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 lombok.extern.slf4j.Slf4j;
import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher;
import org.mockito.Mockito; 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.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
@ -59,6 +61,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
@SpyBean @SpyBean
protected AuditLogService auditLogService; protected AuditLogService auditLogService;
@MockBean
BuildProperties buildProperties;
protected final String msgErrorPermission = "You don't have permission to perform this operation!"; protected final String msgErrorPermission = "You don't have permission to perform this operation!";
protected final String msgErrorShouldBeSpecified = "should be specified"; protected final String msgErrorShouldBeSpecified = "should be specified";
protected final String msgErrorNotFound = "Requested item wasn't found!"; protected final String msgErrorNotFound = "Requested item wasn't found!";

3
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -1268,7 +1268,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
TenantProfile oldTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); TenantProfile oldTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID);
TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile); TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile);
updater.accept(tenantProfile); updater.accept(tenantProfile);
tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile); // 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);
} }
protected OAuth2Client createOauth2Client(TenantId tenantId, String title) { protected OAuth2Client createOauth2Client(TenantId tenantId, String title) {

94
application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest.java

@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.audit.AuditLog;
@ -111,48 +112,49 @@ public class AuditLogControllerTest extends AbstractControllerTest {
doPost("/api/device", device, Device.class); doPost("/api/device", device, Device.class);
} }
List<AuditLog> loadedAuditLogs = new ArrayList<>(); List<AuditLog> loadedAuditLogs = getAuditLogs(5, "/api/audit/logs?");
TimePageLink pageLink = new TimePageLink(5);
PageData<AuditLog> pageData;
do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs?",
new TypeReference<PageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Assert.assertEquals(11 + 1, loadedAuditLogs.size()); Assert.assertEquals(11 + 1, loadedAuditLogs.size());
loadedAuditLogs = new ArrayList<>(); loadedAuditLogs = getAuditLogs(5, "/api/audit/logs/customer/" + ModelConstants.NULL_UUID + "?");
pageLink = new TimePageLink(5);
do { Assert.assertEquals(11 + 1, loadedAuditLogs.size());
pageData = doGetTypedWithTimePageLink("/api/audit/logs/customer/" + ModelConstants.NULL_UUID + "?",
new TypeReference<PageData<AuditLog>>() { loadedAuditLogs = getAuditLogs(5, "/api/audit/logs/user/" + tenantAdmin.getId().getId().toString() + "?");
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Assert.assertEquals(11 + 1, loadedAuditLogs.size()); Assert.assertEquals(11 + 1, loadedAuditLogs.size());
}
@Test
public void testAuditLogsSysAdmin() throws Exception {
loginSysAdmin();
List<AuditLog> loadedAuditLogsBefore = getAuditLogs(100, "/api/audit/logs?");
for (int i = 0; i < 3; i++) {
TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setName("Profile " + UUID.randomUUID());
doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
}
loadedAuditLogs = new ArrayList<>(); List<AuditLog> loadedAuditLogs = getAuditLogs(100, "/api/audit/logs?");
pageLink = new TimePageLink(5);
Assert.assertEquals("Have X audit log before this test + New tenant profiles in the test", loadedAuditLogsBefore.size() + 3, loadedAuditLogs.size());
}
private List<AuditLog> getAuditLogs(int pageSize, String urlTemplate) throws Exception {
List<AuditLog> loadedAuditLogs = new ArrayList<>();
TimePageLink pageLink = new TimePageLink(pageSize);
PageData<AuditLog> pageData;
do { do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs/user/" + tenantAdmin.getId().getId().toString() + "?", pageData = doGetTypedWithTimePageLink(urlTemplate,
new TypeReference<PageData<AuditLog>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedAuditLogs.addAll(pageData.getData()); loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
return loadedAuditLogs;
Assert.assertEquals(11 + 1, loadedAuditLogs.size());
} }
@Test @Test
@ -166,22 +168,32 @@ public class AuditLogControllerTest extends AbstractControllerTest {
savedDevice = doPost("/api/device", savedDevice, Device.class); savedDevice = doPost("/api/device", savedDevice, Device.class);
} }
List<AuditLog> loadedAuditLogs = new ArrayList<>(); List<AuditLog> loadedAuditLogs = getAuditLogs(5, "/api/audit/logs/entity/DEVICE/" + savedDevice.getId().getId() + "?");
TimePageLink pageLink = new TimePageLink(5);
PageData<AuditLog> pageData;
do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs/entity/DEVICE/" + savedDevice.getId().getId() + "?",
new TypeReference<PageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Assert.assertEquals(11 + 1, loadedAuditLogs.size()); Assert.assertEquals(11 + 1, loadedAuditLogs.size());
} }
@Test
public void testAuditLogs_byTenantIdAndEntityId_Sysadmin() throws Exception {
loginSysAdmin();
//created
TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setName("Profile " + UUID.randomUUID());
tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
//updated
tenantProfile.setName(tenantProfile.getName() + "(old)");
tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
List<AuditLog> loadedAuditLogs = getAuditLogs(5, "/api/audit/logs/entity/" +tenantProfile.getId().getEntityType()+ "/" + tenantProfile.getId().getId() + "?");
Assert.assertEquals("Audit logs count by Tenant Profile entity", 2, loadedAuditLogs.size());
//cleanup
doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString());
}
@Test @Test
public void whenSavingNewAuditLog_thenCheckAndCreatePartitionIfNotExists() throws ParseException { public void whenSavingNewAuditLog_thenCheckAndCreatePartitionIfNotExists() throws ParseException {
long entityTs = ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2024-01-01T01:43:11Z").getTime(); long entityTs = ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2024-01-01T01:43:11Z").getTime();

31
application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.awaitility.Awaitility;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher;
@ -25,9 +26,12 @@ import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; 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.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
@ -47,6 +51,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -81,12 +86,17 @@ public class TenantProfileControllerTest extends AbstractControllerTest {
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1); 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"); savedTenantProfile.setName("New tenant profile");
doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class); doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class);
TenantProfile foundTenantProfile = doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString(), TenantProfile.class); TenantProfile foundTenantProfile = doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString(), TenantProfile.class);
Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.UPDATED, 1); testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.UPDATED, 1);
awaitAuditLog("Wait for async audit log to be persisted (UPDATED expected)", savedTenantProfile.getId(), ActionType.UPDATED);
} }
@Test @Test
@ -180,6 +190,8 @@ public class TenantProfileControllerTest extends AbstractControllerTest {
Assert.assertNotNull(foundDefaultTenantProfile); Assert.assertNotNull(foundDefaultTenantProfile);
Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfile.getName()); Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfile.getName());
Assert.assertEquals(savedTenantProfile.getId(), foundDefaultTenantProfile.getId()); 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 @Test
@ -245,6 +257,8 @@ public class TenantProfileControllerTest extends AbstractControllerTest {
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isOk()); .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); testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.DELETED, 1);
doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
@ -393,6 +407,22 @@ public class TenantProfileControllerTest extends AbstractControllerTest {
testBroadcastEntityStateChangeEventNeverTenantProfile(); 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<PageData<AuditLog>>() {
},
new TimePageLink(5))
.getData()
.stream()
.anyMatch(log -> log.getActionType() == expectedAction)
);
}
private TenantProfile createTenantProfile(String name) { private TenantProfile createTenantProfile(String name) {
TenantProfile tenantProfile = new TenantProfile(); TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setName(name); tenantProfile.setName(name);
@ -429,7 +459,6 @@ public class TenantProfileControllerTest extends AbstractControllerTest {
tenantProfile.setProfileData(profileData); tenantProfile.setProfileData(profileData);
} }
private void testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(TenantProfile tenantProfile, ComponentLifecycleEvent event, int cntTime) { private void testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(TenantProfile tenantProfile, ComponentLifecycleEvent event, int cntTime) {
ArgumentMatcher<TenantProfile> matcherTenantProfile = cntTime == 1 ? argument -> argument.equals(tenantProfile) : ArgumentMatcher<TenantProfile> matcherTenantProfile = cntTime == 1 ? argument -> argument.equals(tenantProfile) :
argument -> argument.getClass().equals(TenantProfile.class); argument -> argument.getClass().equals(TenantProfile.class);

3
common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.audit; package org.thingsboard.server.dao.audit;
import com.google.common.util.concurrent.ListenableFuture; 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.HasName;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.audit.AuditLog;
@ -43,7 +44,7 @@ public interface AuditLogService {
CustomerId customerId, CustomerId customerId,
UserId userId, UserId userId,
String userName, String userName,
I entityId, @NotNull I entityId,
E entity, E entity,
ActionType actionType, ActionType actionType,
Exception e, Object... additionalInfo); Exception e, Object... additionalInfo);

2
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); EntityInfo findDefaultTenantProfileInfo(TenantId tenantId);
boolean setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId); TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId);
void deleteTenantProfiles(TenantId tenantId); void deleteTenantProfiles(TenantId tenantId);

2
common/edge-api/src/main/proto/edge.proto

@ -46,7 +46,9 @@ enum EdgeVersion {
V_4_2_0 = 12; V_4_2_0 = 12;
V_4_3_0 = 13; V_4_3_0 = 13;
V_4_2_1_2 = 14; V_4_2_1_2 = 14;
V_4_2_1_3 = 420;
V_4_3_0_1 = 15; V_4_3_0_1 = 15;
V_4_3_0_2 = 430;
V_LATEST = 999; V_LATEST = 999;
} }

5
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.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -117,9 +118,9 @@ public class AuditLogServiceImpl implements AuditLogService {
@Override @Override
public <E extends HasName, I extends EntityId> ListenableFuture<Void> public <E extends HasName, I extends EntityId> ListenableFuture<Void>
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) { ActionType actionType, Exception e, Object... additionalInfo) {
if (canLog(entityId.getEntityType(), actionType)) { if (canLog(entityId.getEntityType(), actionType) || (tenantId != null && tenantId.isSysTenantId())) {
JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo); JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo);
ActionStatus actionStatus = ActionStatus.SUCCESS; ActionStatus actionStatus = ActionStatus.SUCCESS;
String failureDetails = ""; String failureDetails = "";

12
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java

@ -190,7 +190,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService<Tenant
} }
@Override @Override
public boolean setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) { public TenantProfile setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) {
log.trace("Executing setDefaultTenantProfile [{}]", tenantProfileId); log.trace("Executing setDefaultTenantProfile [{}]", tenantProfileId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id); validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id);
@ -198,22 +198,18 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService<Tenant
if (!tenantProfile.isDefault()) { if (!tenantProfile.isDefault()) {
tenantProfile.setDefault(true); tenantProfile.setDefault(true);
TenantProfile previousDefaultTenantProfile = findDefaultTenantProfile(tenantId); TenantProfile previousDefaultTenantProfile = findDefaultTenantProfile(tenantId);
boolean changed = false;
if (previousDefaultTenantProfile == null) { if (previousDefaultTenantProfile == null) {
tenantProfileDao.save(tenantId, tenantProfile); tenantProfile = tenantProfileDao.save(tenantId, tenantProfile);
publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, true)); publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, true));
changed = true;
} else if (!previousDefaultTenantProfile.getId().equals(tenantProfile.getId())) { } else if (!previousDefaultTenantProfile.getId().equals(tenantProfile.getId())) {
previousDefaultTenantProfile.setDefault(false); previousDefaultTenantProfile.setDefault(false);
tenantProfileDao.save(tenantId, previousDefaultTenantProfile); tenantProfileDao.save(tenantId, previousDefaultTenantProfile);
tenantProfileDao.save(tenantId, tenantProfile); tenantProfile = tenantProfileDao.save(tenantId, tenantProfile);
publishEvictEvent(new TenantProfileEvictEvent(previousDefaultTenantProfile.getId(), false)); publishEvictEvent(new TenantProfileEvictEvent(previousDefaultTenantProfile.getId(), false));
publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, true)); publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, true));
changed = true;
} }
return changed;
} }
return false; return tenantProfile;
} }
@Override @Override

8
dao/src/test/java/org/thingsboard/server/dao/service/TenantProfileServiceTest.java

@ -164,13 +164,13 @@ public class TenantProfileServiceTest extends AbstractServiceTest {
TenantProfile savedTenantProfile1 = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile1); TenantProfile savedTenantProfile1 = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile1);
TenantProfile savedTenantProfile2 = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile2); TenantProfile savedTenantProfile2 = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile2);
boolean result = tenantProfileService.setDefaultTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile1.getId()); TenantProfile setDefaultTenantProfile1 = tenantProfileService.setDefaultTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile1.getId());
Assert.assertTrue(result); Assert.assertNotEquals(savedTenantProfile1.isDefault(), setDefaultTenantProfile1.isDefault());
TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID);
Assert.assertNotNull(defaultTenantProfile); Assert.assertNotNull(defaultTenantProfile);
Assert.assertEquals(savedTenantProfile1.getId(), defaultTenantProfile.getId()); Assert.assertEquals(savedTenantProfile1.getId(), defaultTenantProfile.getId());
result = tenantProfileService.setDefaultTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile2.getId()); TenantProfile setDefaultTenantProfile2 = tenantProfileService.setDefaultTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile2.getId());
Assert.assertTrue(result); Assert.assertNotEquals(savedTenantProfile2.isDefault(), setDefaultTenantProfile2.isDefault());
defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID);
Assert.assertNotNull(defaultTenantProfile); Assert.assertNotNull(defaultTenantProfile);
Assert.assertEquals(savedTenantProfile2.getId(), defaultTenantProfile.getId()); Assert.assertEquals(savedTenantProfile2.getId(), defaultTenantProfile.getId());

3
ui-ngx/src/app/core/services/menu.models.ts

@ -816,7 +816,8 @@ const defaultUserMenuMap = new Map<Authority, MenuReference[]>([
{id: MenuId.domains}, {id: MenuId.domains},
{id: MenuId.clients} {id: MenuId.clients}
] ]
} },
{id: MenuId.audit_log}
] ]
} }
] ]

2
ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts

@ -25,7 +25,7 @@ export const auditLogsRoutes: Routes = [
path: 'auditLogs', path: 'auditLogs',
component: AuditLogTableComponent, component: AuditLogTableComponent,
data: { data: {
auth: [Authority.TENANT_ADMIN], auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN],
title: 'audit-log.audit-logs', title: 'audit-log.audit-logs',
breadcrumb: { breadcrumb: {
menuId: MenuId.audit_log menuId: MenuId.audit_log

4
ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html

@ -31,4 +31,8 @@
[entityName]="entity.name"> [entityName]="entity.name">
</tb-attribute-table> </tb-attribute-table>
</mat-tab> </mat-tab>
<mat-tab *ngIf="!isEdit" #auditLogsTab="matTab"
label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table>
</mat-tab>
} }

Loading…
Cancel
Save