Commit 92fdbbf89c6e411a6fa14dc4badcd63a35f4e116

Authored by Volodymyr Babak
1 parent fad04b0d

Code review fixes

@@ -15,9 +15,12 @@ @@ -15,9 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils; 21 import org.apache.commons.lang3.StringUtils;
20 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.security.core.Authentication; 24 import org.springframework.security.core.Authentication;
22 import org.springframework.security.core.context.SecurityContextHolder; 25 import org.springframework.security.core.context.SecurityContextHolder;
23 import org.springframework.web.bind.annotation.ExceptionHandler; 26 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -27,6 +30,8 @@ import org.thingsboard.server.common.data.alarm.Alarm; @@ -27,6 +30,8 @@ import org.thingsboard.server.common.data.alarm.Alarm;
27 import org.thingsboard.server.common.data.alarm.AlarmId; 30 import org.thingsboard.server.common.data.alarm.AlarmId;
28 import org.thingsboard.server.common.data.alarm.AlarmInfo; 31 import org.thingsboard.server.common.data.alarm.AlarmInfo;
29 import org.thingsboard.server.common.data.asset.Asset; 32 import org.thingsboard.server.common.data.asset.Asset;
  33 +import org.thingsboard.server.common.data.audit.ActionStatus;
  34 +import org.thingsboard.server.common.data.audit.ActionType;
30 import org.thingsboard.server.common.data.id.*; 35 import org.thingsboard.server.common.data.id.*;
31 import org.thingsboard.server.common.data.page.TextPageLink; 36 import org.thingsboard.server.common.data.page.TextPageLink;
32 import org.thingsboard.server.common.data.page.TimePageLink; 37 import org.thingsboard.server.common.data.page.TimePageLink;
@@ -73,6 +78,10 @@ public abstract class BaseController { @@ -73,6 +78,10 @@ public abstract class BaseController {
73 78
74 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; 79 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
75 public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; 80 public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!";
  81 +
  82 + @Value("${audit_log.exceptions.enabled}")
  83 + private boolean auditLogExceptionsEnabled;
  84 +
76 @Autowired 85 @Autowired
77 private ThingsboardErrorResponseHandler errorResponseHandler; 86 private ThingsboardErrorResponseHandler errorResponseHandler;
78 87
@@ -131,6 +140,11 @@ public abstract class BaseController { @@ -131,6 +140,11 @@ public abstract class BaseController {
131 return handleException(exception, true); 140 return handleException(exception, true);
132 } 141 }
133 142
  143 + ThingsboardException handleException(Exception exception, ActionType actionType, String actionData) {
  144 + logExceptionToAuditLog(exception, actionType, actionData);
  145 + return handleException(exception, true);
  146 + }
  147 +
134 private ThingsboardException handleException(Exception exception, boolean logException) { 148 private ThingsboardException handleException(Exception exception, boolean logException) {
135 if (logException) { 149 if (logException) {
136 log.error("Error [{}]", exception.getMessage()); 150 log.error("Error [{}]", exception.getMessage());
@@ -153,6 +167,36 @@ public abstract class BaseController { @@ -153,6 +167,36 @@ public abstract class BaseController {
153 } 167 }
154 } 168 }
155 169
  170 + private void logExceptionToAuditLog(Exception exception, ActionType actionType, String actionData) {
  171 + try {
  172 + if (auditLogExceptionsEnabled) {
  173 + SecurityUser currentUser = getCurrentUser();
  174 + EntityId entityId;
  175 + CustomerId customerId;
  176 + if (!currentUser.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  177 + entityId = currentUser.getCustomerId();
  178 + customerId = currentUser.getCustomerId();
  179 + } else {
  180 + entityId = currentUser.getTenantId();
  181 + customerId = new CustomerId(ModelConstants.NULL_UUID);
  182 + }
  183 +
  184 + JsonNode actionDataNode = new ObjectMapper().createObjectNode().put("actionData", actionData);
  185 +
  186 + auditLogService.logEntityAction(currentUser,
  187 + entityId,
  188 + null,
  189 + customerId,
  190 + actionType,
  191 + actionDataNode,
  192 + ActionStatus.FAILURE,
  193 + exception.getMessage());
  194 + }
  195 + } catch (Exception e) {
  196 + log.error("Exception happend during saving to audit log", e);
  197 + }
  198 + }
  199 +
156 <T> T checkNotNull(T reference) throws ThingsboardException { 200 <T> T checkNotNull(T reference) throws ThingsboardException {
157 if (reference == null) { 201 if (reference == null) {
158 throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); 202 throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
@@ -545,4 +589,24 @@ public abstract class BaseController { @@ -545,4 +589,24 @@ public abstract class BaseController {
545 serverPort); 589 serverPort);
546 return baseUrl; 590 return baseUrl;
547 } 591 }
  592 +
  593 + protected void logEntityDeleted(EntityId entityId, String entityName, CustomerId customerId) throws ThingsboardException {
  594 + logEntitySuccess(entityId, entityName, customerId, ActionType.DELETED);
  595 + }
  596 +
  597 + protected void logEntityAddedOrUpdated(EntityId entityId, String entityName, CustomerId customerId, boolean isAddAction) throws ThingsboardException {
  598 + logEntitySuccess(entityId, entityName, customerId, isAddAction ? ActionType.ADDED : ActionType.UPDATED);
  599 + }
  600 +
  601 + protected void logEntitySuccess(EntityId entityId, String entityName, CustomerId customerId, ActionType actionType) throws ThingsboardException {
  602 + auditLogService.logEntityAction(
  603 + getCurrentUser(),
  604 + entityId,
  605 + entityName,
  606 + customerId,
  607 + actionType,
  608 + null,
  609 + ActionStatus.SUCCESS,
  610 + null);
  611 + }
548 } 612 }
@@ -85,20 +85,11 @@ public class DeviceController extends BaseController { @@ -85,20 +85,11 @@ public class DeviceController extends BaseController {
85 savedDevice.getName(), 85 savedDevice.getName(),
86 savedDevice.getType()); 86 savedDevice.getType());
87 87
88 - auditLogService.logEntityAction(  
89 - getCurrentUser(),  
90 - savedDevice.getId(),  
91 - savedDevice.getName(),  
92 - savedDevice.getCustomerId(),  
93 - device.getId() == null ? ActionType.ADDED : ActionType.UPDATED,  
94 - null,  
95 - ActionStatus.SUCCESS,  
96 - null);  
97 - 88 + logEntityAddedOrUpdated(savedDevice.getId(), savedDevice.getName(), savedDevice.getCustomerId(), device.getId() == null);
98 89
99 return savedDevice; 90 return savedDevice;
100 } catch (Exception e) { 91 } catch (Exception e) {
101 - throw handleException(e); 92 + throw handleException(e, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, "addDevice(" + device + ")");
102 } 93 }
103 } 94 }
104 95
@@ -111,17 +102,9 @@ public class DeviceController extends BaseController { @@ -111,17 +102,9 @@ public class DeviceController extends BaseController {
111 DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); 102 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
112 Device device = checkDeviceId(deviceId); 103 Device device = checkDeviceId(deviceId);
113 deviceService.deleteDevice(deviceId); 104 deviceService.deleteDevice(deviceId);
114 - auditLogService.logEntityAction(  
115 - getCurrentUser(),  
116 - device.getId(),  
117 - device.getName(),  
118 - device.getCustomerId(),  
119 - ActionType.DELETED,  
120 - null,  
121 - ActionStatus.SUCCESS,  
122 - null); 105 + logEntityDeleted(device.getId(), device.getName(), device.getCustomerId());
123 } catch (Exception e) { 106 } catch (Exception e) {
124 - throw handleException(e); 107 + throw handleException(e, ActionType.DELETED, "deleteDevice(" + strDeviceId + ")");
125 } 108 }
126 } 109 }
127 110
@@ -200,18 +183,10 @@ public class DeviceController extends BaseController { @@ -200,18 +183,10 @@ public class DeviceController extends BaseController {
200 Device device = checkDeviceId(deviceCredentials.getDeviceId()); 183 Device device = checkDeviceId(deviceCredentials.getDeviceId());
201 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials)); 184 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials));
202 actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()); 185 actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId());
203 - auditLogService.logEntityAction(  
204 - getCurrentUser(),  
205 - device.getId(),  
206 - device.getName(),  
207 - device.getCustomerId(),  
208 - ActionType.CREDENTIALS_UPDATED,  
209 - null,  
210 - ActionStatus.SUCCESS,  
211 - null); 186 + logEntitySuccess(device.getId(), device.getName(), device.getCustomerId(), ActionType.CREDENTIALS_UPDATED);
212 return result; 187 return result;
213 } catch (Exception e) { 188 } catch (Exception e) {
214 - throw handleException(e); 189 + throw handleException(e, ActionType.CREDENTIALS_UPDATED, "saveDeviceCredentials(" + deviceCredentials + ")");
215 } 190 }
216 } 191 }
217 192
@@ -286,4 +286,11 @@ spring: @@ -286,4 +286,11 @@ spring:
286 # Audit log parameters 286 # Audit log parameters
287 audit_log: 287 audit_log:
288 # Enable/disable audit log functionality. 288 # Enable/disable audit log functionality.
289 - enabled: "${AUDIT_LOG_ENABLED:true}"  
  289 + enabled: "${AUDIT_LOG_ENABLED:true}"
  290 + # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS
  291 + by_tenant_partitioning: "${AUDIT_LOG_BY_TENANT_PARTITIONING:MONTHS}"
  292 + # Number of days as history period if startTime and endTime are not specified
  293 + default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}"
  294 + exceptions:
  295 + # Enable/disable audit log functionality for exceptions.
  296 + enabled: "${AUDIT_LOG_EXCEPTIONS_ENABLED:true}"
@@ -81,10 +81,13 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo @@ -81,10 +81,13 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
81 81
82 protected ExecutorService readResultsProcessingExecutor; 82 protected ExecutorService readResultsProcessingExecutor;
83 83
84 - @Value("${cassandra.query.ts_key_value_partitioning}") 84 + @Value("${audit_log.by_tenant_partitioning}")
85 private String partitioning; 85 private String partitioning;
86 private TsPartitionDate tsFormat; 86 private TsPartitionDate tsFormat;
87 87
  88 + @Value("${audit_log.default_query_period}")
  89 + private Integer defaultQueryPeriodInDays;
  90 +
88 private PreparedStatement partitionInsertStmt; 91 private PreparedStatement partitionInsertStmt;
89 private PreparedStatement saveByTenantStmt; 92 private PreparedStatement saveByTenantStmt;
90 private PreparedStatement saveByTenantIdAndUserIdStmt; 93 private PreparedStatement saveByTenantIdAndUserIdStmt;
@@ -304,7 +307,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo @@ -304,7 +307,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
304 if (pageLink.getStartTime() != null && pageLink.getStartTime() != 0) { 307 if (pageLink.getStartTime() != null && pageLink.getStartTime() != 0) {
305 minPartition = toPartitionTs(pageLink.getStartTime()); 308 minPartition = toPartitionTs(pageLink.getStartTime());
306 } else { 309 } else {
307 - minPartition = toPartitionTs(LocalDate.now().minusMonths(1).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli()); 310 + minPartition = toPartitionTs(LocalDate.now().minusDays(defaultQueryPeriodInDays).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
308 } 311 }
309 312
310 long maxPartition; 313 long maxPartition;
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.dao.audit; 16 package org.thingsboard.server.dao.audit;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
20 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 21 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
21 import org.thingsboard.server.common.data.User; 22 import org.thingsboard.server.common.data.User;
@@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.id.UserId; @@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.id.UserId;
29 import org.thingsboard.server.common.data.page.TimePageData; 30 import org.thingsboard.server.common.data.page.TimePageData;
30 import org.thingsboard.server.common.data.page.TimePageLink; 31 import org.thingsboard.server.common.data.page.TimePageLink;
31 32
  33 +import java.util.Collections;
32 import java.util.List; 34 import java.util.List;
33 35
34 @ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "false") 36 @ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "false")
@@ -36,26 +38,26 @@ public class DummyAuditLogServiceImpl implements AuditLogService { @@ -36,26 +38,26 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
36 38
37 @Override 39 @Override
38 public TimePageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) { 40 public TimePageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) {
39 - return null; 41 + return new TimePageData<>(null, pageLink);
40 } 42 }
41 43
42 @Override 44 @Override
43 public TimePageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, TimePageLink pageLink) { 45 public TimePageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, TimePageLink pageLink) {
44 - return null; 46 + return new TimePageData<>(null, pageLink);
45 } 47 }
46 48
47 @Override 49 @Override
48 public TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink) { 50 public TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink) {
49 - return null; 51 + return new TimePageData<>(null, pageLink);
50 } 52 }
51 53
52 @Override 54 @Override
53 public TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink) { 55 public TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink) {
54 - return null; 56 + return new TimePageData<>(null, pageLink);
55 } 57 }
56 58
57 @Override 59 @Override
58 public ListenableFuture<List<Void>> logEntityAction(User user, EntityId entityId, String entityName, CustomerId customerId, ActionType actionType, JsonNode actionData, ActionStatus actionStatus, String actionFailureDetails) { 60 public ListenableFuture<List<Void>> logEntityAction(User user, EntityId entityId, String entityName, CustomerId customerId, ActionType actionType, JsonNode actionData, ActionStatus actionStatus, String actionFailureDetails) {
59 - return null; 61 + return Futures.immediateFuture(Collections.emptyList());
60 } 62 }
61 } 63 }
@@ -10,6 +10,9 @@ zk.zk_dir=/thingsboard @@ -10,6 +10,9 @@ zk.zk_dir=/thingsboard
10 updates.enabled=false 10 updates.enabled=false
11 11
12 audit_log.enabled=true 12 audit_log.enabled=true
  13 +audit_log.exceptions.enabled=false
  14 +audit_log.by_tenant_partitioning=MONTHS
  15 +audit_log.default_query_period=30
13 16
14 caching.specs.relations.timeToLiveInMinutes=1440 17 caching.specs.relations.timeToLiveInMinutes=1440
15 caching.specs.relations.maxSize=100000 18 caching.specs.relations.maxSize=100000