Commit 92fdbbf89c6e411a6fa14dc4badcd63a35f4e116

Authored by Volodymyr Babak
1 parent fad04b0d

Code review fixes

... ... @@ -15,9 +15,12 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
18 20 import lombok.extern.slf4j.Slf4j;
19 21 import org.apache.commons.lang3.StringUtils;
20 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
21 24 import org.springframework.security.core.Authentication;
22 25 import org.springframework.security.core.context.SecurityContextHolder;
23 26 import org.springframework.web.bind.annotation.ExceptionHandler;
... ... @@ -27,6 +30,8 @@ import org.thingsboard.server.common.data.alarm.Alarm;
27 30 import org.thingsboard.server.common.data.alarm.AlarmId;
28 31 import org.thingsboard.server.common.data.alarm.AlarmInfo;
29 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 35 import org.thingsboard.server.common.data.id.*;
31 36 import org.thingsboard.server.common.data.page.TextPageLink;
32 37 import org.thingsboard.server.common.data.page.TimePageLink;
... ... @@ -73,6 +78,10 @@ public abstract class BaseController {
73 78
74 79 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
75 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 85 @Autowired
77 86 private ThingsboardErrorResponseHandler errorResponseHandler;
78 87
... ... @@ -131,6 +140,11 @@ public abstract class BaseController {
131 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 148 private ThingsboardException handleException(Exception exception, boolean logException) {
135 149 if (logException) {
136 150 log.error("Error [{}]", exception.getMessage());
... ... @@ -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 200 <T> T checkNotNull(T reference) throws ThingsboardException {
157 201 if (reference == null) {
158 202 throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
... ... @@ -545,4 +589,24 @@ public abstract class BaseController {
545 589 serverPort);
546 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 85 savedDevice.getName(),
86 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 90 return savedDevice;
100 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 102 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
112 103 Device device = checkDeviceId(deviceId);
113 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 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 183 Device device = checkDeviceId(deviceCredentials.getDeviceId());
201 184 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials));
202 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 187 return result;
213 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 286 # Audit log parameters
287 287 audit_log:
288 288 # Enable/disable audit log functionality.
289   - enabled: "${AUDIT_LOG_ENABLED:true}"
\ No newline at end of file
  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}"
\ No newline at end of file
... ...
... ... @@ -81,10 +81,13 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
81 81
82 82 protected ExecutorService readResultsProcessingExecutor;
83 83
84   - @Value("${cassandra.query.ts_key_value_partitioning}")
  84 + @Value("${audit_log.by_tenant_partitioning}")
85 85 private String partitioning;
86 86 private TsPartitionDate tsFormat;
87 87
  88 + @Value("${audit_log.default_query_period}")
  89 + private Integer defaultQueryPeriodInDays;
  90 +
88 91 private PreparedStatement partitionInsertStmt;
89 92 private PreparedStatement saveByTenantStmt;
90 93 private PreparedStatement saveByTenantIdAndUserIdStmt;
... ... @@ -304,7 +307,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
304 307 if (pageLink.getStartTime() != null && pageLink.getStartTime() != 0) {
305 308 minPartition = toPartitionTs(pageLink.getStartTime());
306 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 313 long maxPartition;
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.dao.audit;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.common.util.concurrent.Futures;
19 20 import com.google.common.util.concurrent.ListenableFuture;
20 21 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
21 22 import org.thingsboard.server.common.data.User;
... ... @@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.id.UserId;
29 30 import org.thingsboard.server.common.data.page.TimePageData;
30 31 import org.thingsboard.server.common.data.page.TimePageLink;
31 32
  33 +import java.util.Collections;
32 34 import java.util.List;
33 35
34 36 @ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "false")
... ... @@ -36,26 +38,26 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
36 38
37 39 @Override
38 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 44 @Override
43 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 49 @Override
48 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 54 @Override
53 55 public TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink) {
54   - return null;
  56 + return new TimePageData<>(null, pageLink);
55 57 }
56 58
57 59 @Override
58 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 10 updates.enabled=false
11 11
12 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 17 caching.specs.relations.timeToLiveInMinutes=1440
15 18 caching.specs.relations.maxSize=100000
... ...