thingsboard-aplcache
Changes
application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java 40(+35 -5)
dao/src/main/resources/cassandra/schema.cql 34(+34 -0)
Details
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 b79359b..4d1d50e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
@@ -18,20 +18,64 @@ package org.thingsboard.server.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.audit.AuditLog;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.exception.ThingsboardException;
+import java.util.UUID;
+
@RestController
@RequestMapping("/api")
public class AuditLogController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/audit/logs/{entityType}/{entityId}", params = {"limit"}, method = RequestMethod.GET)
+ @RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
- public TimePageData<AuditLog> getAuditLogs(
+ public TimePageData<AuditLog> getAuditLogsByCustomerId(
+ @PathVariable("customerId") String strCustomerId,
+ @RequestParam int limit,
+ @RequestParam(required = false) Long startTime,
+ @RequestParam(required = false) Long endTime,
+ @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
+ @RequestParam(required = false) String offset) throws ThingsboardException {
+ try {
+ checkParameter("CustomerId", strCustomerId);
+ TenantId tenantId = getCurrentUser().getTenantId();
+ TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
+ return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), pageLink));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/audit/logs/user/{userId}", params = {"limit"}, method = RequestMethod.GET)
+ @ResponseBody
+ public TimePageData<AuditLog> getAuditLogsByUserId(
+ @PathVariable("userId") String strUserId,
+ @RequestParam int limit,
+ @RequestParam(required = false) Long startTime,
+ @RequestParam(required = false) Long endTime,
+ @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
+ @RequestParam(required = false) String offset) throws ThingsboardException {
+ try {
+ checkParameter("UserId", strUserId);
+ TenantId tenantId = getCurrentUser().getTenantId();
+ TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
+ return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), pageLink));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"limit"}, method = RequestMethod.GET)
+ @ResponseBody
+ public TimePageData<AuditLog> getAuditLogsByEntityId(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam int limit,
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index 8d08706..0844ce0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -85,12 +85,16 @@ public class DeviceController extends BaseController {
savedDevice.getName(),
savedDevice.getType());
- // TODO: refactor to ANNOTATION usage
- if (device.getId() == null) {
- logDeviceAction(savedDevice, ActionType.ADDED);
- } else {
- logDeviceAction(savedDevice, ActionType.UPDATED);
- }
+ auditLogService.logEntityAction(
+ getCurrentUser(),
+ savedDevice.getId(),
+ savedDevice.getName(),
+ savedDevice.getCustomerId(),
+ device.getId() == null ? ActionType.ADDED : ActionType.UPDATED,
+ null,
+ ActionStatus.SUCCESS,
+ null);
+
return savedDevice;
} catch (Exception e) {
@@ -107,8 +111,15 @@ public class DeviceController extends BaseController {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId);
deviceService.deleteDevice(deviceId);
- // TODO: refactor to ANNOTATION usage
- logDeviceAction(device, ActionType.DELETED);
+ auditLogService.logEntityAction(
+ getCurrentUser(),
+ device.getId(),
+ device.getName(),
+ device.getCustomerId(),
+ ActionType.DELETED,
+ null,
+ ActionStatus.SUCCESS,
+ null);
} catch (Exception e) {
throw handleException(e);
}
@@ -189,8 +200,15 @@ public class DeviceController extends BaseController {
Device device = checkDeviceId(deviceCredentials.getDeviceId());
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials));
actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId());
- // TODO: refactor to ANNOTATION usage
- logDeviceAction(device, ActionType.CREDENTIALS_UPDATED);
+ auditLogService.logEntityAction(
+ getCurrentUser(),
+ device.getId(),
+ device.getName(),
+ device.getCustomerId(),
+ ActionType.CREDENTIALS_UPDATED,
+ null,
+ ActionStatus.SUCCESS,
+ null);
return result;
} catch (Exception e) {
throw handleException(e);
@@ -321,19 +339,4 @@ public class DeviceController extends BaseController {
throw handleException(e);
}
}
-
- // TODO: refactor to ANNOTATION usage
- private void logDeviceAction(Device device, ActionType actionType) throws ThingsboardException {
- auditLogService.logAction(
- getCurrentUser().getTenantId(),
- device.getId(),
- device.getName(),
- device.getCustomerId(),
- getCurrentUser().getId(),
- getCurrentUser().getName(),
- actionType,
- null,
- ActionStatus.SUCCESS,
- null);
- }
}
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 83630ce..3d8501f 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -244,7 +244,6 @@ spring:
username: "${SPRING_DATASOURCE_USERNAME:sa}"
password: "${SPRING_DATASOURCE_PASSWORD:}"
-
# PostgreSQL DAO Configuration
#spring:
# data:
@@ -260,3 +259,8 @@ spring:
# url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}"
# username: "${SPRING_DATASOURCE_USERNAME:postgres}"
# password: "${SPRING_DATASOURCE_PASSWORD:postgres}"
+
+# Audit log parameters
+audit_log:
+ # Enable/disable audit log functionality.
+ enabled: "${AUDIT_LOG_ENABLED:true}"
\ No newline at end of file
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java
index 5e74416..a826ee8 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
+import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public abstract class BaseAuditLogControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
+ private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
@@ -46,14 +48,14 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
- User tenantAdmin = new User();
+ tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
- createUserAndLogin(tenantAdmin, "testPassword1");
+ tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@After
@@ -65,7 +67,7 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
}
@Test
- public void testSaveDeviceAuditLogs() throws Exception {
+ public void testAuditLogs() throws Exception {
for (int i = 0; i < 178; i++) {
Device device = new Device();
device.setName("Device" + i);
@@ -87,10 +89,38 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
} while (pageData.hasNext());
Assert.assertEquals(178, loadedAuditLogs.size());
+
+ loadedAuditLogs = new ArrayList<>();
+ pageLink = new TimePageLink(23);
+ do {
+ pageData = doGetTypedWithTimePageLink("/api/audit/logs/customer/" + ModelConstants.NULL_UUID + "?",
+ new TypeReference<TimePageData<AuditLog>>() {
+ }, pageLink);
+ loadedAuditLogs.addAll(pageData.getData());
+ if (pageData.hasNext()) {
+ pageLink = pageData.getNextPageLink();
+ }
+ } while (pageData.hasNext());
+
+ Assert.assertEquals(178, loadedAuditLogs.size());
+
+ loadedAuditLogs = new ArrayList<>();
+ pageLink = new TimePageLink(23);
+ do {
+ pageData = doGetTypedWithTimePageLink("/api/audit/logs/user/" + tenantAdmin.getId().getId().toString() + "?",
+ new TypeReference<TimePageData<AuditLog>>() {
+ }, pageLink);
+ loadedAuditLogs.addAll(pageData.getData());
+ if (pageData.hasNext()) {
+ pageLink = pageData.getNextPageLink();
+ }
+ } while (pageData.hasNext());
+
+ Assert.assertEquals(178, loadedAuditLogs.size());
}
@Test
- public void testUpdateDeviceAuditLogs() throws Exception {
+ public void testAuditLogs_byTenantIdAndEntityId() throws Exception {
Device device = new Device();
device.setName("Device name");
device.setType("default");
@@ -104,7 +134,7 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
TimePageLink pageLink = new TimePageLink(23);
TimePageData<AuditLog> pageData;
do {
- pageData = doGetTypedWithTimePageLink("/api/audit/logs/DEVICE/" + savedDevice.getId().getId() + "?",
+ pageData = doGetTypedWithTimePageLink("/api/audit/logs/entity/DEVICE/" + savedDevice.getId().getId() + "?",
new TypeReference<TimePageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java
index f401ad1..8562b6a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java
@@ -17,7 +17,9 @@ package org.thingsboard.server.dao.audit;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.audit.AuditLog;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
@@ -29,9 +31,17 @@ public interface AuditLogDao {
ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog);
+ ListenableFuture<Void> saveByTenantIdAndCustomerId(AuditLog auditLog);
+
+ ListenableFuture<Void> saveByTenantIdAndUserId(AuditLog auditLog);
+
ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog);
List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink);
+ List<AuditLog> findAuditLogsByTenantIdAndCustomerId(UUID tenantId, CustomerId customerId, TimePageLink pageLink);
+
+ List<AuditLog> findAuditLogsByTenantIdAndUserId(UUID tenantId, UserId userId, TimePageLink pageLink);
+
List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogQueryCursor.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogQueryCursor.java
new file mode 100644
index 0000000..78b043b
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogQueryCursor.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.audit;
+
+import lombok.Getter;
+import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.dao.model.nosql.AuditLogEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class AuditLogQueryCursor {
+ @Getter
+ private final UUID tenantId;
+ @Getter
+ private final List<AuditLogEntity> data;
+ @Getter
+ private final TimePageLink pageLink;
+
+ private final List<Long> partitions;
+
+ private int partitionIndex;
+ private int currentLimit;
+
+ public AuditLogQueryCursor(UUID tenantId, TimePageLink pageLink, List<Long> partitions) {
+ this.tenantId = tenantId;
+ this.partitions = partitions;
+ this.partitionIndex = partitions.size() - 1;
+ this.data = new ArrayList<>();
+ this.currentLimit = pageLink.getLimit();
+ this.pageLink = pageLink;
+ }
+
+ public boolean hasNextPartition() {
+ return partitionIndex >= 0;
+ }
+
+ public boolean isFull() {
+ return currentLimit <= 0;
+ }
+
+ public long getNextPartition() {
+ long partition = partitions.get(partitionIndex);
+ partitionIndex--;
+ return partition;
+ }
+
+ public int getCurrentLimit() {
+ return currentLimit;
+ }
+
+ public void addData(List<AuditLogEntity> newData) {
+ currentLimit -= newData.size();
+ data.addAll(newData);
+ }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
index 5593ede..d6e61ab 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.audit;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
@@ -31,20 +32,21 @@ import java.util.List;
public interface AuditLogService {
+ TimePageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink);
+
+ TimePageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, TimePageLink pageLink);
+
TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink);
TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink);
- ListenableFuture<List<Void>> logAction(TenantId tenantId,
- EntityId entityId,
- String entityName,
- CustomerId customerId,
- UserId userId,
- String userName,
- ActionType actionType,
- JsonNode actionData,
- ActionStatus actionStatus,
- String actionFailureDetails);
-
+ ListenableFuture<List<Void>> logEntityAction(User user,
+ EntityId entityId,
+ String entityName,
+ CustomerId customerId,
+ ActionType actionType,
+ JsonNode actionData,
+ ActionStatus actionStatus,
+ String actionFailureDetails);
}
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 1baf573..1ee26e5 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
@@ -15,20 +15,20 @@
*/
package org.thingsboard.server.dao.audit;
+import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
-import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
@@ -41,6 +41,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Slf4j
@Service
+@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "true")
public class AuditLogServiceImpl implements AuditLogService {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@@ -50,6 +51,24 @@ public class AuditLogServiceImpl implements AuditLogService {
private AuditLogDao auditLogDao;
@Override
+ public TimePageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) {
+ log.trace("Executing findAuditLogsByTenantIdAndCustomerId [{}], [{}], [{}]", tenantId, customerId, pageLink);
+ validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
+ validateId(customerId, "Incorrect customerId " + customerId);
+ List<AuditLog> auditLogs = auditLogDao.findAuditLogsByTenantIdAndCustomerId(tenantId.getId(), customerId, pageLink);
+ return new TimePageData<>(auditLogs, pageLink);
+ }
+
+ @Override
+ public TimePageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, TimePageLink pageLink) {
+ log.trace("Executing findAuditLogsByTenantIdAndUserId [{}], [{}], [{}]", tenantId, userId, pageLink);
+ validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
+ validateId(userId, "Incorrect userId" + userId);
+ List<AuditLog> auditLogs = auditLogDao.findAuditLogsByTenantIdAndUserId(tenantId.getId(), userId, pageLink);
+ return new TimePageData<>(auditLogs, pageLink);
+ }
+
+ @Override
public TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink) {
log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
@@ -66,6 +85,28 @@ public class AuditLogServiceImpl implements AuditLogService {
return new TimePageData<>(auditLogs, pageLink);
}
+ @Override
+ public ListenableFuture<List<Void>> logEntityAction(User user,
+ EntityId entityId,
+ String entityName,
+ CustomerId customerId,
+ ActionType actionType,
+ JsonNode actionData,
+ ActionStatus actionStatus,
+ String actionFailureDetails) {
+ return logAction(
+ user.getTenantId(),
+ entityId,
+ entityName,
+ customerId,
+ user.getId(),
+ user.getName(),
+ actionType,
+ actionData,
+ actionStatus,
+ actionFailureDetails);
+ }
+
private AuditLog createAuditLogEntry(TenantId tenantId,
EntityId entityId,
String entityName,
@@ -77,6 +118,7 @@ public class AuditLogServiceImpl implements AuditLogService {
ActionStatus actionStatus,
String actionFailureDetails) {
AuditLog result = new AuditLog();
+ result.setId(new AuditLogId(UUIDs.timeBased()));
result.setTenantId(tenantId);
result.setEntityId(entityId);
result.setEntityName(entityName);
@@ -90,17 +132,16 @@ public class AuditLogServiceImpl implements AuditLogService {
return result;
}
- @Override
- public ListenableFuture<List<Void>> logAction(TenantId tenantId,
- EntityId entityId,
- String entityName,
- CustomerId customerId,
- UserId userId,
- String userName,
- ActionType actionType,
- JsonNode actionData,
- ActionStatus actionStatus,
- String actionFailureDetails) {
+ private ListenableFuture<List<Void>> logAction(TenantId tenantId,
+ EntityId entityId,
+ String entityName,
+ CustomerId customerId,
+ UserId userId,
+ String userName,
+ ActionType actionType,
+ JsonNode actionData,
+ ActionStatus actionStatus,
+ String actionFailureDetails) {
AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName,
actionType, actionData, actionStatus, actionFailureDetails);
log.trace("Executing logAction [{}]", auditLogEntry);
@@ -109,6 +150,8 @@ public class AuditLogServiceImpl implements AuditLogService {
futures.add(auditLogDao.savePartitionsByTenantId(auditLogEntry));
futures.add(auditLogDao.saveByTenantId(auditLogEntry));
futures.add(auditLogDao.saveByTenantIdAndEntityId(auditLogEntry));
+ futures.add(auditLogDao.saveByTenantIdAndCustomerId(auditLogEntry));
+ futures.add(auditLogDao.saveByTenantIdAndUserId(auditLogEntry));
return Futures.allAsList(futures);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
index 2fcb732..8262933 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
@@ -19,7 +19,8 @@ import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
-import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.datastax.driver.core.querybuilder.Select;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -29,8 +30,9 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.audit.AuditLog;
-import org.thingsboard.server.common.data.id.AuditLogId;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.ModelConstants;
@@ -52,6 +54,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static org.thingsboard.server.dao.model.ModelConstants.*;
@@ -82,7 +85,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
private String partitioning;
private TsPartitionDate tsFormat;
- private PreparedStatement[] saveStmts;
+ private PreparedStatement partitionInsertStmt;
+ private PreparedStatement saveByTenantStmt;
+ private PreparedStatement saveByTenantIdAndUserIdStmt;
+ private PreparedStatement saveByTenantIdAndEntityIdStmt;
+ private PreparedStatement saveByTenantIdAndCustomerIdStmt;
private boolean isInstall() {
return environment.acceptsProfiles("install");
@@ -123,11 +130,9 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
public ListenableFuture<Void> saveByTenantId(AuditLog auditLog) {
log.debug("Save saveByTenantId [{}] ", auditLog);
- AuditLogId auditLogId = new AuditLogId(UUIDs.timeBased());
-
long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
BoundStatement stmt = getSaveByTenantStmt().bind();
- stmt.setUUID(0, auditLogId.getId())
+ stmt = stmt.setUUID(0, auditLog.getId().getId())
.setUUID(1, auditLog.getTenantId().getId())
.setUUID(2, auditLog.getEntityId().getId())
.setString(3, auditLog.getEntityId().getEntityType().name())
@@ -140,18 +145,45 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
public ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog) {
log.debug("Save saveByTenantIdAndEntityId [{}] ", auditLog);
- AuditLogId auditLogId = new AuditLogId(UUIDs.timeBased());
-
BoundStatement stmt = getSaveByTenantIdAndEntityIdStmt().bind();
- stmt.setUUID(0, auditLogId.getId())
- .setUUID(1, auditLog.getTenantId().getId())
- .setUUID(2, auditLog.getEntityId().getId())
- .setString(3, auditLog.getEntityId().getEntityType().name())
- .setString(4, auditLog.getActionType().name());
+ stmt = setSaveStmtVariables(stmt, auditLog);
return getFuture(executeAsyncWrite(stmt), rs -> null);
}
@Override
+ public ListenableFuture<Void> saveByTenantIdAndCustomerId(AuditLog auditLog) {
+ log.debug("Save saveByTenantIdAndCustomerId [{}] ", auditLog);
+
+ BoundStatement stmt = getSaveByTenantIdAndCustomerIdStmt().bind();
+ stmt = setSaveStmtVariables(stmt, auditLog);
+ return getFuture(executeAsyncWrite(stmt), rs -> null);
+ }
+
+ @Override
+ public ListenableFuture<Void> saveByTenantIdAndUserId(AuditLog auditLog) {
+ log.debug("Save saveByTenantIdAndUserId [{}] ", auditLog);
+
+ BoundStatement stmt = getSaveByTenantIdAndUserIdStmt().bind();
+ stmt = setSaveStmtVariables(stmt, auditLog);
+ return getFuture(executeAsyncWrite(stmt), rs -> null);
+ }
+
+ private BoundStatement setSaveStmtVariables(BoundStatement stmt, AuditLog auditLog) {
+ return stmt.setUUID(0, auditLog.getId().getId())
+ .setUUID(1, auditLog.getTenantId().getId())
+ .setUUID(2, auditLog.getCustomerId().getId())
+ .setUUID(3, auditLog.getEntityId().getId())
+ .setString(4, auditLog.getEntityId().getEntityType().name())
+ .setString(5, auditLog.getEntityName())
+ .setUUID(6, auditLog.getUserId().getId())
+ .setString(7, auditLog.getUserName())
+ .setString(8, auditLog.getActionType().name())
+ .setString(9, auditLog.getActionData() != null ? auditLog.getActionData().toString() : null)
+ .setString(10, auditLog.getActionStatus().name())
+ .setString(11, auditLog.getActionFailureDetails());
+ }
+
+ @Override
public ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog) {
log.debug("Save savePartitionsByTenantId [{}] ", auditLog);
@@ -163,35 +195,66 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
return getFuture(executeAsyncWrite(stmt), rs -> null);
}
- private PreparedStatement getPartitionInsertStmt() {
- // TODO: ADD CACHE LOGIC
- return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF +
- "(" + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
- "," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" +
- " VALUES(?, ?)");
+ private PreparedStatement getSaveByTenantIdAndEntityIdStmt() {
+ if (saveByTenantIdAndEntityIdStmt == null) {
+ saveByTenantIdAndEntityIdStmt = getSaveByTenantIdAndCFName(ModelConstants.AUDIT_LOG_BY_ENTITY_ID_CF);
+ }
+ return saveByTenantIdAndEntityIdStmt;
}
- private PreparedStatement getSaveByTenantIdAndEntityIdStmt() {
- // TODO: ADD CACHE LOGIC
- return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_ENTITY_ID_CF +
- "(" + ModelConstants.AUDIT_LOG_ID_PROPERTY +
- "," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
- "," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY +
- "," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY +
- "," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY + ")" +
- " VALUES(?, ?, ?, ?, ?)");
+ private PreparedStatement getSaveByTenantIdAndCustomerIdStmt() {
+ if (saveByTenantIdAndCustomerIdStmt == null) {
+ saveByTenantIdAndCustomerIdStmt = getSaveByTenantIdAndCFName(ModelConstants.AUDIT_LOG_BY_CUSTOMER_ID_CF);
+ }
+ return saveByTenantIdAndCustomerIdStmt;
}
- private PreparedStatement getSaveByTenantStmt() {
- // TODO: ADD CACHE LOGIC
- return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_CF +
+ private PreparedStatement getSaveByTenantIdAndUserIdStmt() {
+ if (saveByTenantIdAndUserIdStmt == null) {
+ saveByTenantIdAndUserIdStmt = getSaveByTenantIdAndCFName(ModelConstants.AUDIT_LOG_BY_USER_ID_CF);
+ }
+ return saveByTenantIdAndUserIdStmt;
+ }
+
+ private PreparedStatement getSaveByTenantIdAndCFName(String cfName) {
+ return getSession().prepare(INSERT_INTO + cfName +
"(" + ModelConstants.AUDIT_LOG_ID_PROPERTY +
"," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY +
"," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY +
"," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_ENTITY_NAME_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_USER_ID_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_USER_NAME_PROPERTY +
"," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY +
- "," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" +
- " VALUES(?, ?, ?, ?, ?, ?)");
+ "," + ModelConstants.AUDIT_LOG_ACTION_DATA_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_ACTION_STATUS_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY + ")" +
+ " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ }
+
+ private PreparedStatement getPartitionInsertStmt() {
+ if (partitionInsertStmt == null) {
+ partitionInsertStmt = getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF +
+ "(" + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" +
+ " VALUES(?, ?)");
+ }
+ return partitionInsertStmt;
+ }
+
+ private PreparedStatement getSaveByTenantStmt() {
+ if (saveByTenantStmt == null) {
+ saveByTenantStmt = getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_CF +
+ "(" + ModelConstants.AUDIT_LOG_ID_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY +
+ "," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" +
+ " VALUES(?, ?, ?, ?, ?, ?)");
+ }
+ return saveByTenantStmt;
}
private long toPartitionTs(long ts) {
@@ -199,7 +262,6 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
return tsFormat.truncatedTo(time).toInstant(ZoneOffset.UTC).toEpochMilli();
}
-
@Override
public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) {
log.trace("Try to find audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink);
@@ -213,30 +275,75 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
}
@Override
+ public List<AuditLog> findAuditLogsByTenantIdAndCustomerId(UUID tenantId, CustomerId customerId, TimePageLink pageLink) {
+ log.trace("Try to find audit logs by tenant [{}], customer [{}] and pageLink [{}]", tenantId, customerId, pageLink);
+ List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_CUSTOMER_ID_CF,
+ Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
+ eq(ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY, customerId.getId())),
+ pageLink);
+ log.trace("Found audit logs by tenant [{}], customer [{}] and pageLink [{}]", tenantId, customerId, pageLink);
+ return DaoUtil.convertDataList(entities);
+ }
+
+ @Override
+ public List<AuditLog> findAuditLogsByTenantIdAndUserId(UUID tenantId, UserId userId, TimePageLink pageLink) {
+ log.trace("Try to find audit logs by tenant [{}], user [{}] and pageLink [{}]", tenantId, userId, pageLink);
+ List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_USER_ID_CF,
+ Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
+ eq(ModelConstants.AUDIT_LOG_USER_ID_PROPERTY, userId.getId())),
+ pageLink);
+ log.trace("Found audit logs by tenant [{}], user [{}] and pageLink [{}]", tenantId, userId, pageLink);
+ return DaoUtil.convertDataList(entities);
+ }
+
+ @Override
public List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink) {
log.trace("Try to find audit logs by tenant [{}] and pageLink [{}]", tenantId, pageLink);
- // TODO: ADD AUDIT LOG PARTITION CURSOR LOGIC
-
long minPartition;
- long maxPartition;
-
if (pageLink.getStartTime() != null && pageLink.getStartTime() != 0) {
minPartition = toPartitionTs(pageLink.getStartTime());
} else {
minPartition = toPartitionTs(LocalDate.now().minusMonths(1).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
}
+ long maxPartition;
if (pageLink.getEndTime() != null && pageLink.getEndTime() != 0) {
maxPartition = toPartitionTs(pageLink.getEndTime());
} else {
maxPartition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
}
- List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_TENANT_ID_CF,
- Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
- eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, maxPartition)),
- pageLink);
+
+ List<Long> partitions = fetchPartitions(tenantId, minPartition, maxPartition)
+ .all()
+ .stream()
+ .map(row -> row.getLong(ModelConstants.PARTITION_COLUMN))
+ .collect(Collectors.toList());
+
+ AuditLogQueryCursor cursor = new AuditLogQueryCursor(tenantId, pageLink, partitions);
+ List<AuditLogEntity> entities = fetchSequentiallyWithLimit(cursor);
log.trace("Found audit logs by tenant [{}] and pageLink [{}]", tenantId, pageLink);
return DaoUtil.convertDataList(entities);
}
+
+ private List<AuditLogEntity> fetchSequentiallyWithLimit(AuditLogQueryCursor cursor) {
+ if (cursor.isFull() || !cursor.hasNextPartition()) {
+ return cursor.getData();
+ } else {
+ cursor.addData(findPageWithTimeSearch(AUDIT_LOG_BY_TENANT_ID_CF,
+ Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, cursor.getTenantId()),
+ eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, cursor.getNextPartition())),
+ cursor.getPageLink()));
+ return fetchSequentiallyWithLimit(cursor);
+ }
+ }
+
+ private ResultSet fetchPartitions(UUID tenantId, long minPartition, long maxPartition) {
+ Select.Where select = QueryBuilder.select(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY).from(ModelConstants.AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF)
+ .where(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId));
+ select.and(QueryBuilder.gte(ModelConstants.PARTITION_COLUMN, minPartition));
+ select.and(QueryBuilder.lte(ModelConstants.PARTITION_COLUMN, maxPartition));
+ return getSession().execute(select);
+ }
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java
new file mode 100644
index 0000000..c610769
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.audit;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.audit.ActionStatus;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.audit.AuditLog;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
+
+import java.util.List;
+
+@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "false")
+public class DummyAuditLogServiceImpl implements AuditLogService {
+
+ @Override
+ public TimePageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) {
+ return null;
+ }
+
+ @Override
+ public TimePageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, TimePageLink pageLink) {
+ return null;
+ }
+
+ @Override
+ public TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink) {
+ return null;
+ }
+
+ @Override
+ public TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink) {
+ return null;
+ }
+
+ @Override
+ public ListenableFuture<List<Void>> logEntityAction(User user, EntityId entityId, String entityName, CustomerId customerId, ActionType actionType, JsonNode actionData, ActionStatus actionStatus, String actionFailureDetails) {
+ return null;
+ }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index 58c8d2f..66e03b0 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
@@ -147,6 +147,8 @@ public class ModelConstants {
public static final String AUDIT_LOG_COLUMN_FAMILY_NAME = "audit_log";
public static final String AUDIT_LOG_BY_ENTITY_ID_CF = "audit_log_by_entity_id";
+ public static final String AUDIT_LOG_BY_CUSTOMER_ID_CF = "audit_log_by_customer_id";
+ public static final String AUDIT_LOG_BY_USER_ID_CF = "audit_log_by_user_id";
public static final String AUDIT_LOG_BY_TENANT_ID_CF = "audit_log_by_tenant_id";
public static final String AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF = "audit_log_by_tenant_id_partitions";
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java
index 7e4fdc1..bac44c5 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java
@@ -41,4 +41,20 @@ public interface AuditLogRepository extends CrudRepository<AuditLogEntity, Strin
@Param("entityType") EntityType entityType,
@Param("idOffset") String idOffset,
Pageable pageable);
+
+ @Query("SELECT al FROM AuditLogEntity al WHERE al.tenantId = :tenantId " +
+ "AND al.customerId = :customerId " +
+ "AND al.id > :idOffset ORDER BY al.id")
+ List<AuditLogEntity> findByTenantIdAndCustomerId(@Param("tenantId") String tenantId,
+ @Param("customerId") String customerId,
+ @Param("idOffset") String idOffset,
+ Pageable pageable);
+
+ @Query("SELECT al FROM AuditLogEntity al WHERE al.tenantId = :tenantId " +
+ "AND al.userId = :userId " +
+ "AND al.id > :idOffset ORDER BY al.id")
+ List<AuditLogEntity> findByTenantIdAndUserId(@Param("tenantId") String tenantId,
+ @Param("userId") String userId,
+ @Param("idOffset") String idOffset,
+ Pageable pageable);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java
index fe7edf8..b4abf07 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java
@@ -23,7 +23,9 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.audit.AuditLog;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.audit.AuditLogDao;
@@ -77,6 +79,16 @@ public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> imp
}
@Override
+ public ListenableFuture<Void> saveByTenantIdAndCustomerId(AuditLog auditLog) {
+ return insertService.submit(() -> null);
+ }
+
+ @Override
+ public ListenableFuture<Void> saveByTenantIdAndUserId(AuditLog auditLog) {
+ return insertService.submit(() -> null);
+ }
+
+ @Override
public ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog) {
return insertService.submit(() -> null);
}
@@ -93,6 +105,26 @@ public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> imp
}
@Override
+ public List<AuditLog> findAuditLogsByTenantIdAndCustomerId(UUID tenantId, CustomerId customerId, TimePageLink pageLink) {
+ return DaoUtil.convertDataList(
+ auditLogRepository.findByTenantIdAndCustomerId(
+ fromTimeUUID(tenantId),
+ fromTimeUUID(customerId.getId()),
+ pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
+ new PageRequest(0, pageLink.getLimit())));
+ }
+
+ @Override
+ public List<AuditLog> findAuditLogsByTenantIdAndUserId(UUID tenantId, UserId userId, TimePageLink pageLink) {
+ return DaoUtil.convertDataList(
+ auditLogRepository.findByTenantIdAndUserId(
+ fromTimeUUID(tenantId),
+ fromTimeUUID(userId.getId()),
+ pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
+ new PageRequest(0, pageLink.getLimit())));
+ }
+
+ @Override
public List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink) {
return DaoUtil.convertDataList(
auditLogRepository.findByTenantId(
dao/src/main/resources/cassandra/schema.cql 34(+34 -0)
diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql
index 6dc2bec..bc7884d 100644
--- a/dao/src/main/resources/cassandra/schema.cql
+++ b/dao/src/main/resources/cassandra/schema.cql
@@ -566,6 +566,40 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id (
PRIMARY KEY ((tenant_id, entity_id, entity_type), id)
);
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_customer_id (
+ tenant_id timeuuid,
+ id timeuuid,
+ customer_id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ entity_name text,
+ user_id timeuuid,
+ user_name text,
+ action_type text,
+ action_data text,
+ action_status text,
+ action_failure_details text,
+ PRIMARY KEY ((tenant_id, customer_id), id)
+);
+
+CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_user_id (
+ tenant_id timeuuid,
+ id timeuuid,
+ customer_id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ entity_name text,
+ user_id timeuuid,
+ user_name text,
+ action_type text,
+ action_data text,
+ action_status text,
+ action_failure_details text,
+ PRIMARY KEY ((tenant_id, user_id), id)
+);
+
+
+
CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id (
tenant_id timeuuid,
id timeuuid,
diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties
index d87a181..cbca287 100644
--- a/dao/src/test/resources/application-test.properties
+++ b/dao/src/test/resources/application-test.properties
@@ -7,4 +7,6 @@ zk.enabled=false
zk.url=localhost:2181
zk.zk_dir=/thingsboard
-updates.enabled=false
\ No newline at end of file
+updates.enabled=false
+
+audit_log.enabled=true
\ No newline at end of file