Commit 3898e70cb261ca13ef3627b26db0d641d0daa51a

Authored by Volodymyr Babak
1 parent 787f9f94

Added audit log base impl

Showing 27 changed files with 1472 additions and 11 deletions
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.web.bind.annotation.*;
  20 +import org.thingsboard.server.common.data.audit.AuditLog;
  21 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.data.page.TimePageData;
  24 +import org.thingsboard.server.common.data.page.TimePageLink;
  25 +import org.thingsboard.server.exception.ThingsboardException;
  26 +
  27 +@RestController
  28 +@RequestMapping("/api")
  29 +public class AuditLogController extends BaseController {
  30 +
  31 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  32 + @RequestMapping(value = "/audit/logs/{entityType}/{entityId}", params = {"limit"}, method = RequestMethod.GET)
  33 + @ResponseBody
  34 + public TimePageData<AuditLog> getAuditLogs(
  35 + @PathVariable("entityType") String strEntityType,
  36 + @PathVariable("entityId") String strEntityId,
  37 + @RequestParam int limit,
  38 + @RequestParam(required = false) Long startTime,
  39 + @RequestParam(required = false) Long endTime,
  40 + @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
  41 + @RequestParam(required = false) String offset) throws ThingsboardException {
  42 + try {
  43 + checkParameter("EntityId", strEntityId);
  44 + checkParameter("EntityType", strEntityType);
  45 + TenantId tenantId = getCurrentUser().getTenantId();
  46 + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
  47 + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), pageLink));
  48 + } catch (Exception e) {
  49 + throw handleException(e);
  50 + }
  51 + }
  52 +
  53 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  54 + @RequestMapping(value = "/audit/logs", params = {"limit"}, method = RequestMethod.GET)
  55 + @ResponseBody
  56 + public TimePageData<AuditLog> getAuditLogs(
  57 + @RequestParam int limit,
  58 + @RequestParam(required = false) Long startTime,
  59 + @RequestParam(required = false) Long endTime,
  60 + @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
  61 + @RequestParam(required = false) String offset) throws ThingsboardException {
  62 + try {
  63 + TenantId tenantId = getCurrentUser().getTenantId();
  64 + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
  65 + return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, pageLink));
  66 + } catch (Exception e) {
  67 + throw handleException(e);
  68 + }
  69 + }
  70 +}
... ...
... ... @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.widget.WidgetType;
39 39 import org.thingsboard.server.common.data.widget.WidgetsBundle;
40 40 import org.thingsboard.server.dao.alarm.AlarmService;
41 41 import org.thingsboard.server.dao.asset.AssetService;
  42 +import org.thingsboard.server.dao.audit.AuditLogService;
42 43 import org.thingsboard.server.dao.customer.CustomerService;
43 44 import org.thingsboard.server.dao.dashboard.DashboardService;
44 45 import org.thingsboard.server.dao.device.DeviceCredentialsService;
... ... @@ -117,6 +118,9 @@ public abstract class BaseController {
117 118 @Autowired
118 119 protected RelationService relationService;
119 120
  121 + @Autowired
  122 + protected AuditLogService auditLogService;
  123 +
120 124
121 125 @ExceptionHandler(ThingsboardException.class)
122 126 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
... ...
... ... @@ -22,6 +22,9 @@ import org.springframework.web.bind.annotation.*;
22 22 import org.thingsboard.server.common.data.Customer;
23 23 import org.thingsboard.server.common.data.Device;
24 24 import org.thingsboard.server.common.data.EntitySubtype;
  25 +import org.thingsboard.server.common.data.audit.ActionStatus;
  26 +import org.thingsboard.server.common.data.audit.ActionType;
  27 +import org.thingsboard.server.common.data.device.DeviceSearchQuery;
25 28 import org.thingsboard.server.common.data.id.CustomerId;
26 29 import org.thingsboard.server.common.data.id.DeviceId;
27 30 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -29,7 +32,6 @@ import org.thingsboard.server.common.data.page.TextPageData;
29 32 import org.thingsboard.server.common.data.page.TextPageLink;
30 33 import org.thingsboard.server.common.data.security.Authority;
31 34 import org.thingsboard.server.common.data.security.DeviceCredentials;
32   -import org.thingsboard.server.common.data.device.DeviceSearchQuery;
33 35 import org.thingsboard.server.dao.exception.IncorrectParameterException;
34 36 import org.thingsboard.server.dao.model.ModelConstants;
35 37 import org.thingsboard.server.exception.ThingsboardErrorCode;
... ... @@ -75,12 +77,21 @@ public class DeviceController extends BaseController {
75 77 }
76 78 }
77 79 Device savedDevice = checkNotNull(deviceService.saveDevice(device));
  80 +
78 81 actorService
79 82 .onDeviceNameOrTypeUpdate(
80 83 savedDevice.getTenantId(),
81 84 savedDevice.getId(),
82 85 savedDevice.getName(),
83 86 savedDevice.getType());
  87 +
  88 + // TODO: refactor to ANNOTATION usage
  89 + if (device.getId() == null) {
  90 + logDeviceAction(savedDevice, ActionType.ADDED);
  91 + } else {
  92 + logDeviceAction(savedDevice, ActionType.UPDATED);
  93 + }
  94 +
84 95 return savedDevice;
85 96 } catch (Exception e) {
86 97 throw handleException(e);
... ... @@ -94,8 +105,10 @@ public class DeviceController extends BaseController {
94 105 checkParameter(DEVICE_ID, strDeviceId);
95 106 try {
96 107 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
97   - checkDeviceId(deviceId);
  108 + Device device = checkDeviceId(deviceId);
98 109 deviceService.deleteDevice(deviceId);
  110 + // TODO: refactor to ANNOTATION usage
  111 + logDeviceAction(device, ActionType.DELETED);
99 112 } catch (Exception e) {
100 113 throw handleException(e);
101 114 }
... ... @@ -173,9 +186,11 @@ public class DeviceController extends BaseController {
173 186 public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException {
174 187 checkNotNull(deviceCredentials);
175 188 try {
176   - checkDeviceId(deviceCredentials.getDeviceId());
  189 + Device device = checkDeviceId(deviceCredentials.getDeviceId());
177 190 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials));
178 191 actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId());
  192 + // TODO: refactor to ANNOTATION usage
  193 + logDeviceAction(device, ActionType.CREDENTIALS_UPDATED);
179 194 return result;
180 195 } catch (Exception e) {
181 196 throw handleException(e);
... ... @@ -307,4 +322,18 @@ public class DeviceController extends BaseController {
307 322 }
308 323 }
309 324
  325 + // TODO: refactor to ANNOTATION usage
  326 + private void logDeviceAction(Device device, ActionType actionType) throws ThingsboardException {
  327 + auditLogService.logAction(
  328 + getCurrentUser().getTenantId(),
  329 + device.getId(),
  330 + device.getName(),
  331 + device.getCustomerId(),
  332 + getCurrentUser().getId(),
  333 + getCurrentUser().getName(),
  334 + actionType,
  335 + null,
  336 + ActionStatus.SUCCESS,
  337 + null);
  338 + }
310 339 }
... ...
... ... @@ -66,6 +66,7 @@ import org.thingsboard.server.common.data.User;
66 66 import org.thingsboard.server.common.data.id.TenantId;
67 67 import org.thingsboard.server.common.data.id.UUIDBased;
68 68 import org.thingsboard.server.common.data.page.TextPageLink;
  69 +import org.thingsboard.server.common.data.page.TimePageLink;
69 70 import org.thingsboard.server.common.data.security.Authority;
70 71 import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
71 72 import org.thingsboard.server.service.mail.TestMailService;
... ... @@ -331,6 +332,35 @@ public abstract class AbstractControllerTest {
331 332 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
332 333 }
333 334
  335 + protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
  336 + TimePageLink pageLink,
  337 + Object... urlVariables) throws Exception {
  338 + List<Object> pageLinkVariables = new ArrayList<>();
  339 + urlTemplate += "limit={limit}";
  340 + pageLinkVariables.add(pageLink.getLimit());
  341 + if (pageLink.getStartTime() != null) {
  342 + urlTemplate += "&startTime={startTime}";
  343 + pageLinkVariables.add(pageLink.getStartTime());
  344 + }
  345 + if (pageLink.getEndTime() != null) {
  346 + urlTemplate += "&endTime={endTime}";
  347 + pageLinkVariables.add(pageLink.getEndTime());
  348 + }
  349 + if (pageLink.getIdOffset() != null) {
  350 + urlTemplate += "&offset={offset}";
  351 + pageLinkVariables.add(pageLink.getIdOffset().toString());
  352 + }
  353 + if (pageLink.isAscOrder()) {
  354 + urlTemplate += "&ascOrder={ascOrder}";
  355 + pageLinkVariables.add(pageLink.isAscOrder());
  356 + }
  357 + Object[] vars = new Object[urlVariables.length + pageLinkVariables.size()];
  358 + System.arraycopy(urlVariables, 0, vars, 0, urlVariables.length);
  359 + System.arraycopy(pageLinkVariables.toArray(), 0, vars, urlVariables.length, pageLinkVariables.size());
  360 +
  361 + return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
  362 + }
  363 +
334 364 protected <T> T doPost(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
335 365 return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass);
336 366 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import com.fasterxml.jackson.core.type.TypeReference;
  19 +import org.junit.After;
  20 +import org.junit.Assert;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.Tenant;
  25 +import org.thingsboard.server.common.data.User;
  26 +import org.thingsboard.server.common.data.audit.AuditLog;
  27 +import org.thingsboard.server.common.data.page.TimePageData;
  28 +import org.thingsboard.server.common.data.page.TimePageLink;
  29 +import org.thingsboard.server.common.data.security.Authority;
  30 +
  31 +import java.util.ArrayList;
  32 +import java.util.List;
  33 +
  34 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  35 +
  36 +public abstract class BaseAuditLogControllerTest extends AbstractControllerTest {
  37 +
  38 + private Tenant savedTenant;
  39 +
  40 + @Before
  41 + public void beforeTest() throws Exception {
  42 + loginSysAdmin();
  43 +
  44 + Tenant tenant = new Tenant();
  45 + tenant.setTitle("My tenant");
  46 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  47 + Assert.assertNotNull(savedTenant);
  48 +
  49 + User tenantAdmin = new User();
  50 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  51 + tenantAdmin.setTenantId(savedTenant.getId());
  52 + tenantAdmin.setEmail("tenant2@thingsboard.org");
  53 + tenantAdmin.setFirstName("Joe");
  54 + tenantAdmin.setLastName("Downs");
  55 +
  56 + createUserAndLogin(tenantAdmin, "testPassword1");
  57 + }
  58 +
  59 + @After
  60 + public void afterTest() throws Exception {
  61 + loginSysAdmin();
  62 +
  63 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  64 + .andExpect(status().isOk());
  65 + }
  66 +
  67 + @Test
  68 + public void testSaveDeviceAuditLogs() throws Exception {
  69 + for (int i = 0; i < 178; i++) {
  70 + Device device = new Device();
  71 + device.setName("Device" + i);
  72 + device.setType("default");
  73 + doPost("/api/device", device, Device.class);
  74 + }
  75 +
  76 + List<AuditLog> loadedAuditLogs = new ArrayList<>();
  77 + TimePageLink pageLink = new TimePageLink(23);
  78 + TimePageData<AuditLog> pageData;
  79 + do {
  80 + pageData = doGetTypedWithTimePageLink("/api/audit/logs?",
  81 + new TypeReference<TimePageData<AuditLog>>() {
  82 + }, pageLink);
  83 + loadedAuditLogs.addAll(pageData.getData());
  84 + if (pageData.hasNext()) {
  85 + pageLink = pageData.getNextPageLink();
  86 + }
  87 + } while (pageData.hasNext());
  88 +
  89 + Assert.assertEquals(178, loadedAuditLogs.size());
  90 + }
  91 +
  92 + @Test
  93 + public void testUpdateDeviceAuditLogs() throws Exception {
  94 + Device device = new Device();
  95 + device.setName("Device name");
  96 + device.setType("default");
  97 + Device savedDevice = doPost("/api/device", device, Device.class);
  98 + for (int i = 0; i < 178; i++) {
  99 + savedDevice.setName("Device name" + i);
  100 + doPost("/api/device", savedDevice, Device.class);
  101 + }
  102 +
  103 + List<AuditLog> loadedAuditLogs = new ArrayList<>();
  104 + TimePageLink pageLink = new TimePageLink(23);
  105 + TimePageData<AuditLog> pageData;
  106 + do {
  107 + pageData = doGetTypedWithTimePageLink("/api/audit/logs/DEVICE/" + savedDevice.getId().getId() + "?",
  108 + new TypeReference<TimePageData<AuditLog>>() {
  109 + }, pageLink);
  110 + loadedAuditLogs.addAll(pageData.getData());
  111 + if (pageData.hasNext()) {
  112 + pageLink = pageData.getNextPageLink();
  113 + }
  114 + } while (pageData.hasNext());
  115 +
  116 + Assert.assertEquals(179, loadedAuditLogs.size());
  117 + }
  118 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller.nosql;
  17 +
  18 +import org.thingsboard.server.controller.BaseAuditLogControllerTest;
  19 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  20 +
  21 +@DaoNoSqlTest
  22 +public class AuditLogControllerNoSqlTest extends BaseAuditLogControllerTest {
  23 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller.sql;
  17 +
  18 +import org.thingsboard.server.controller.BaseAuditLogControllerTest;
  19 +import org.thingsboard.server.dao.service.DaoSqlTest;
  20 +
  21 +@DaoSqlTest
  22 +public class AuditLogControllerSqlTest extends BaseAuditLogControllerTest {
  23 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.audit;
  17 +
  18 +public enum ActionStatus {
  19 + SUCCESS, FAILURE
  20 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.audit;
  17 +
  18 +public enum ActionType {
  19 + ADDED, DELETED, UPDATED, ATTRIBUTE_UPDATED, ATTRIBUTE_DELETED, ATTRIBUTE_ADDED, RPC_CALL, CREDENTIALS_UPDATED
  20 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.audit;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Data;
  20 +import lombok.EqualsAndHashCode;
  21 +import org.thingsboard.server.common.data.BaseData;
  22 +import org.thingsboard.server.common.data.id.*;
  23 +
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@Data
  26 +public class AuditLog extends BaseData<AuditLogId> {
  27 +
  28 + private TenantId tenantId;
  29 + private CustomerId customerId;
  30 + private EntityId entityId;
  31 + private String entityName;
  32 + private UserId userId;
  33 + private String userName;
  34 + private ActionType actionType;
  35 + private JsonNode actionData;
  36 + private ActionStatus actionStatus;
  37 + private String actionFailureDetails;
  38 +
  39 + public AuditLog() {
  40 + super();
  41 + }
  42 +
  43 + public AuditLog(AuditLogId id) {
  44 + super(id);
  45 + }
  46 +
  47 + public AuditLog(AuditLog auditLog) {
  48 + super(auditLog);
  49 + this.tenantId = auditLog.getTenantId();
  50 + this.customerId = auditLog.getCustomerId();
  51 + this.entityId = auditLog.getEntityId();
  52 + this.entityName = auditLog.getEntityName();
  53 + this.userId = auditLog.getUserId();
  54 + this.userName = auditLog.getUserName();
  55 + this.actionType = auditLog.getActionType();
  56 + this.actionData = auditLog.getActionData();
  57 + this.actionStatus = auditLog.getActionStatus();
  58 + this.actionFailureDetails = auditLog.getActionFailureDetails();
  59 + }
  60 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +
  21 +import java.util.UUID;
  22 +
  23 +public class AuditLogId extends UUIDBased {
  24 +
  25 + private static final long serialVersionUID = 1L;
  26 +
  27 + @JsonCreator
  28 + public AuditLogId(@JsonProperty("id") UUID id) {
  29 + super(id);
  30 + }
  31 +
  32 + public static AuditLogId fromString(String auditLogId) {
  33 + return new AuditLogId(UUID.fromString(auditLogId));
  34 + }
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.audit;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.audit.AuditLog;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.common.data.page.TimePageLink;
  22 +
  23 +import java.util.List;
  24 +import java.util.UUID;
  25 +
  26 +public interface AuditLogDao {
  27 +
  28 + ListenableFuture<Void> saveByTenantId(AuditLog auditLog);
  29 +
  30 + ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog);
  31 +
  32 + ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog);
  33 +
  34 + List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink);
  35 +
  36 + List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink);
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.audit;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import org.thingsboard.server.common.data.audit.ActionStatus;
  21 +import org.thingsboard.server.common.data.audit.ActionType;
  22 +import org.thingsboard.server.common.data.audit.AuditLog;
  23 +import org.thingsboard.server.common.data.id.CustomerId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.id.UserId;
  27 +import org.thingsboard.server.common.data.page.TimePageData;
  28 +import org.thingsboard.server.common.data.page.TimePageLink;
  29 +
  30 +import java.util.List;
  31 +
  32 +public interface AuditLogService {
  33 +
  34 + TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink);
  35 +
  36 + TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink);
  37 +
  38 + ListenableFuture<List<Void>> logAction(TenantId tenantId,
  39 + EntityId entityId,
  40 + String entityName,
  41 + CustomerId customerId,
  42 + UserId userId,
  43 + String userName,
  44 + ActionType actionType,
  45 + JsonNode actionData,
  46 + ActionStatus actionStatus,
  47 + String actionFailureDetails);
  48 +
  49 +
  50 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.audit;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.common.collect.Lists;
  20 +import com.google.common.util.concurrent.Futures;
  21 +import com.google.common.util.concurrent.ListenableFuture;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.springframework.stereotype.Service;
  25 +import org.thingsboard.server.common.data.audit.ActionStatus;
  26 +import org.thingsboard.server.common.data.audit.ActionType;
  27 +import org.thingsboard.server.common.data.audit.AuditLog;
  28 +import org.thingsboard.server.common.data.id.CustomerId;
  29 +import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
  31 +import org.thingsboard.server.common.data.id.UserId;
  32 +import org.thingsboard.server.common.data.page.TimePageData;
  33 +import org.thingsboard.server.common.data.page.TimePageLink;
  34 +import org.thingsboard.server.dao.exception.DataValidationException;
  35 +import org.thingsboard.server.dao.service.DataValidator;
  36 +
  37 +import java.util.List;
  38 +
  39 +import static org.thingsboard.server.dao.service.Validator.validateEntityId;
  40 +import static org.thingsboard.server.dao.service.Validator.validateId;
  41 +
  42 +@Slf4j
  43 +@Service
  44 +public class AuditLogServiceImpl implements AuditLogService {
  45 +
  46 + private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
  47 + private static final int INSERTS_PER_ENTRY = 3;
  48 +
  49 + @Autowired
  50 + private AuditLogDao auditLogDao;
  51 +
  52 + @Override
  53 + public TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink) {
  54 + log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink);
  55 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  56 + validateEntityId(entityId, INCORRECT_TENANT_ID + entityId);
  57 + List<AuditLog> auditLogs = auditLogDao.findAuditLogsByTenantIdAndEntityId(tenantId.getId(), entityId, pageLink);
  58 + return new TimePageData<>(auditLogs, pageLink);
  59 + }
  60 +
  61 + @Override
  62 + public TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink) {
  63 + log.trace("Executing findAuditLogs [{}]", pageLink);
  64 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  65 + List<AuditLog> auditLogs = auditLogDao.findAuditLogsByTenantId(tenantId.getId(), pageLink);
  66 + return new TimePageData<>(auditLogs, pageLink);
  67 + }
  68 +
  69 + private AuditLog createAuditLogEntry(TenantId tenantId,
  70 + EntityId entityId,
  71 + String entityName,
  72 + CustomerId customerId,
  73 + UserId userId,
  74 + String userName,
  75 + ActionType actionType,
  76 + JsonNode actionData,
  77 + ActionStatus actionStatus,
  78 + String actionFailureDetails) {
  79 + AuditLog result = new AuditLog();
  80 + result.setTenantId(tenantId);
  81 + result.setEntityId(entityId);
  82 + result.setEntityName(entityName);
  83 + result.setCustomerId(customerId);
  84 + result.setUserId(userId);
  85 + result.setUserName(userName);
  86 + result.setActionType(actionType);
  87 + result.setActionData(actionData);
  88 + result.setActionStatus(actionStatus);
  89 + result.setActionFailureDetails(actionFailureDetails);
  90 + return result;
  91 + }
  92 +
  93 + @Override
  94 + public ListenableFuture<List<Void>> logAction(TenantId tenantId,
  95 + EntityId entityId,
  96 + String entityName,
  97 + CustomerId customerId,
  98 + UserId userId,
  99 + String userName,
  100 + ActionType actionType,
  101 + JsonNode actionData,
  102 + ActionStatus actionStatus,
  103 + String actionFailureDetails) {
  104 + AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName,
  105 + actionType, actionData, actionStatus, actionFailureDetails);
  106 + log.trace("Executing logAction [{}]", auditLogEntry);
  107 + auditLogValidator.validate(auditLogEntry);
  108 + List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY);
  109 + futures.add(auditLogDao.savePartitionsByTenantId(auditLogEntry));
  110 + futures.add(auditLogDao.saveByTenantId(auditLogEntry));
  111 + futures.add(auditLogDao.saveByTenantIdAndEntityId(auditLogEntry));
  112 + return Futures.allAsList(futures);
  113 + }
  114 +
  115 + private DataValidator<AuditLog> auditLogValidator =
  116 + new DataValidator<AuditLog>() {
  117 + @Override
  118 + protected void validateDataImpl(AuditLog auditLog) {
  119 + if (auditLog.getEntityId() == null) {
  120 + throw new DataValidationException("Entity Id should be specified!");
  121 + }
  122 + if (auditLog.getTenantId() == null) {
  123 + throw new DataValidationException("Tenant Id should be specified!");
  124 + }
  125 + if (auditLog.getUserId() == null) {
  126 + throw new DataValidationException("User Id should be specified!");
  127 + }
  128 + }
  129 + };
  130 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.audit;
  17 +
  18 +import com.datastax.driver.core.BoundStatement;
  19 +import com.datastax.driver.core.PreparedStatement;
  20 +import com.datastax.driver.core.ResultSet;
  21 +import com.datastax.driver.core.ResultSetFuture;
  22 +import com.datastax.driver.core.utils.UUIDs;
  23 +import com.google.common.base.Function;
  24 +import com.google.common.util.concurrent.Futures;
  25 +import com.google.common.util.concurrent.ListenableFuture;
  26 +import lombok.extern.slf4j.Slf4j;
  27 +import org.springframework.beans.factory.annotation.Autowired;
  28 +import org.springframework.beans.factory.annotation.Value;
  29 +import org.springframework.core.env.Environment;
  30 +import org.springframework.stereotype.Component;
  31 +import org.thingsboard.server.common.data.audit.AuditLog;
  32 +import org.thingsboard.server.common.data.id.AuditLogId;
  33 +import org.thingsboard.server.common.data.id.EntityId;
  34 +import org.thingsboard.server.common.data.page.TimePageLink;
  35 +import org.thingsboard.server.dao.DaoUtil;
  36 +import org.thingsboard.server.dao.model.ModelConstants;
  37 +import org.thingsboard.server.dao.model.nosql.AuditLogEntity;
  38 +import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTimeDao;
  39 +import org.thingsboard.server.dao.timeseries.TsPartitionDate;
  40 +import org.thingsboard.server.dao.util.NoSqlDao;
  41 +
  42 +import javax.annotation.Nullable;
  43 +import javax.annotation.PostConstruct;
  44 +import javax.annotation.PreDestroy;
  45 +import java.time.Instant;
  46 +import java.time.LocalDate;
  47 +import java.time.LocalDateTime;
  48 +import java.time.ZoneOffset;
  49 +import java.util.Arrays;
  50 +import java.util.List;
  51 +import java.util.Optional;
  52 +import java.util.UUID;
  53 +import java.util.concurrent.ExecutorService;
  54 +import java.util.concurrent.Executors;
  55 +
  56 +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
  57 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  58 +
  59 +@Component
  60 +@Slf4j
  61 +@NoSqlDao
  62 +public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLogEntity, AuditLog> implements AuditLogDao {
  63 +
  64 + private static final String INSERT_INTO = "INSERT INTO ";
  65 +
  66 + @Autowired
  67 + private Environment environment;
  68 +
  69 + @Override
  70 + protected Class<AuditLogEntity> getColumnFamilyClass() {
  71 + return AuditLogEntity.class;
  72 + }
  73 +
  74 + @Override
  75 + protected String getColumnFamilyName() {
  76 + return AUDIT_LOG_COLUMN_FAMILY_NAME;
  77 + }
  78 +
  79 + protected ExecutorService readResultsProcessingExecutor;
  80 +
  81 + @Value("${cassandra.query.ts_key_value_partitioning}")
  82 + private String partitioning;
  83 + private TsPartitionDate tsFormat;
  84 +
  85 + private PreparedStatement[] saveStmts;
  86 +
  87 + private boolean isInstall() {
  88 + return environment.acceptsProfiles("install");
  89 + }
  90 +
  91 + @PostConstruct
  92 + public void init() {
  93 + if (!isInstall()) {
  94 + Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning);
  95 + if (partition.isPresent()) {
  96 + tsFormat = partition.get();
  97 + } else {
  98 + log.warn("Incorrect configuration of partitioning {}", partitioning);
  99 + throw new RuntimeException("Failed to parse partitioning property: " + partitioning + "!");
  100 + }
  101 + }
  102 + readResultsProcessingExecutor = Executors.newCachedThreadPool();
  103 + }
  104 +
  105 + @PreDestroy
  106 + public void stopExecutor() {
  107 + if (readResultsProcessingExecutor != null) {
  108 + readResultsProcessingExecutor.shutdownNow();
  109 + }
  110 + }
  111 +
  112 + private <T> ListenableFuture<T> getFuture(ResultSetFuture future, java.util.function.Function<ResultSet, T> transformer) {
  113 + return Futures.transform(future, new Function<ResultSet, T>() {
  114 + @Nullable
  115 + @Override
  116 + public T apply(@Nullable ResultSet input) {
  117 + return transformer.apply(input);
  118 + }
  119 + }, readResultsProcessingExecutor);
  120 + }
  121 +
  122 + @Override
  123 + public ListenableFuture<Void> saveByTenantId(AuditLog auditLog) {
  124 + log.debug("Save saveByTenantId [{}] ", auditLog);
  125 +
  126 + AuditLogId auditLogId = new AuditLogId(UUIDs.timeBased());
  127 +
  128 + long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
  129 + BoundStatement stmt = getSaveByTenantStmt().bind();
  130 + stmt.setUUID(0, auditLogId.getId())
  131 + .setUUID(1, auditLog.getTenantId().getId())
  132 + .setUUID(2, auditLog.getEntityId().getId())
  133 + .setString(3, auditLog.getEntityId().getEntityType().name())
  134 + .setString(4, auditLog.getActionType().name())
  135 + .setLong(5, partition);
  136 + return getFuture(executeAsyncWrite(stmt), rs -> null);
  137 + }
  138 +
  139 + @Override
  140 + public ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog) {
  141 + log.debug("Save saveByTenantIdAndEntityId [{}] ", auditLog);
  142 +
  143 + AuditLogId auditLogId = new AuditLogId(UUIDs.timeBased());
  144 +
  145 + BoundStatement stmt = getSaveByTenantIdAndEntityIdStmt().bind();
  146 + stmt.setUUID(0, auditLogId.getId())
  147 + .setUUID(1, auditLog.getTenantId().getId())
  148 + .setUUID(2, auditLog.getEntityId().getId())
  149 + .setString(3, auditLog.getEntityId().getEntityType().name())
  150 + .setString(4, auditLog.getActionType().name());
  151 + return getFuture(executeAsyncWrite(stmt), rs -> null);
  152 + }
  153 +
  154 + @Override
  155 + public ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog) {
  156 + log.debug("Save savePartitionsByTenantId [{}] ", auditLog);
  157 +
  158 + long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
  159 +
  160 + BoundStatement stmt = getPartitionInsertStmt().bind();
  161 + stmt = stmt.setUUID(0, auditLog.getTenantId().getId())
  162 + .setLong(1, partition);
  163 + return getFuture(executeAsyncWrite(stmt), rs -> null);
  164 + }
  165 +
  166 + private PreparedStatement getPartitionInsertStmt() {
  167 + // TODO: ADD CACHE LOGIC
  168 + return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF +
  169 + "(" + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
  170 + "," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" +
  171 + " VALUES(?, ?)");
  172 + }
  173 +
  174 + private PreparedStatement getSaveByTenantIdAndEntityIdStmt() {
  175 + // TODO: ADD CACHE LOGIC
  176 + return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_ENTITY_ID_CF +
  177 + "(" + ModelConstants.AUDIT_LOG_ID_PROPERTY +
  178 + "," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
  179 + "," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY +
  180 + "," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY +
  181 + "," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY + ")" +
  182 + " VALUES(?, ?, ?, ?, ?)");
  183 + }
  184 +
  185 + private PreparedStatement getSaveByTenantStmt() {
  186 + // TODO: ADD CACHE LOGIC
  187 + return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_CF +
  188 + "(" + ModelConstants.AUDIT_LOG_ID_PROPERTY +
  189 + "," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY +
  190 + "," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY +
  191 + "," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY +
  192 + "," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY +
  193 + "," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" +
  194 + " VALUES(?, ?, ?, ?, ?, ?)");
  195 + }
  196 +
  197 + private long toPartitionTs(long ts) {
  198 + LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts), ZoneOffset.UTC);
  199 + return tsFormat.truncatedTo(time).toInstant(ZoneOffset.UTC).toEpochMilli();
  200 + }
  201 +
  202 +
  203 + @Override
  204 + public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) {
  205 + log.trace("Try to find audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink);
  206 + List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_ENTITY_ID_CF,
  207 + Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
  208 + eq(ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
  209 + eq(ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY, entityId.getId())),
  210 + pageLink);
  211 + log.trace("Found audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink);
  212 + return DaoUtil.convertDataList(entities);
  213 + }
  214 +
  215 + @Override
  216 + public List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink) {
  217 + log.trace("Try to find audit logs by tenant [{}] and pageLink [{}]", tenantId, pageLink);
  218 +
  219 + // TODO: ADD AUDIT LOG PARTITION CURSOR LOGIC
  220 +
  221 + long minPartition;
  222 + long maxPartition;
  223 +
  224 + if (pageLink.getStartTime() != null && pageLink.getStartTime() != 0) {
  225 + minPartition = toPartitionTs(pageLink.getStartTime());
  226 + } else {
  227 + minPartition = toPartitionTs(LocalDate.now().minusMonths(1).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
  228 + }
  229 +
  230 + if (pageLink.getEndTime() != null && pageLink.getEndTime() != 0) {
  231 + maxPartition = toPartitionTs(pageLink.getEndTime());
  232 + } else {
  233 + maxPartition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
  234 + }
  235 + List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_TENANT_ID_CF,
  236 + Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
  237 + eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, maxPartition)),
  238 + pageLink);
  239 + log.trace("Found audit logs by tenant [{}] and pageLink [{}]", tenantId, pageLink);
  240 + return DaoUtil.convertDataList(entities);
  241 + }
  242 +}
... ...
... ... @@ -44,6 +44,13 @@ public class ModelConstants {
44 44 public static final String ADDITIONAL_INFO_PROPERTY = "additional_info";
45 45 public static final String ENTITY_TYPE_PROPERTY = "entity_type";
46 46
  47 + public static final String ENTITY_TYPE_COLUMN = ENTITY_TYPE_PROPERTY;
  48 + public static final String ENTITY_ID_COLUMN = "entity_id";
  49 + public static final String ATTRIBUTE_TYPE_COLUMN = "attribute_type";
  50 + public static final String ATTRIBUTE_KEY_COLUMN = "attribute_key";
  51 + public static final String LAST_UPDATE_TS_COLUMN = "last_update_ts";
  52 +
  53 +
47 54 /**
48 55 * Cassandra user constants.
49 56 */
... ... @@ -135,6 +142,29 @@ public class ModelConstants {
135 142 public static final String DEVICE_TYPES_BY_TENANT_VIEW_NAME = "device_types_by_tenant";
136 143
137 144 /**
  145 + * Cassandra audit log constants.
  146 + */
  147 + public static final String AUDIT_LOG_COLUMN_FAMILY_NAME = "audit_log";
  148 +
  149 + public static final String AUDIT_LOG_BY_ENTITY_ID_CF = "audit_log_by_entity_id";
  150 + public static final String AUDIT_LOG_BY_TENANT_ID_CF = "audit_log_by_tenant_id";
  151 + public static final String AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF = "audit_log_by_tenant_id_partitions";
  152 +
  153 + public static final String AUDIT_LOG_ID_PROPERTY = ID_PROPERTY;
  154 + public static final String AUDIT_LOG_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
  155 + public static final String AUDIT_LOG_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
  156 + public static final String AUDIT_LOG_ENTITY_TYPE_PROPERTY = ENTITY_TYPE_PROPERTY;
  157 + public static final String AUDIT_LOG_ENTITY_ID_PROPERTY = ENTITY_ID_COLUMN;
  158 + public static final String AUDIT_LOG_ENTITY_NAME_PROPERTY = "entity_name";
  159 + public static final String AUDIT_LOG_USER_ID_PROPERTY = USER_ID_PROPERTY;
  160 + public static final String AUDIT_LOG_PARTITION_PROPERTY = "partition";
  161 + public static final String AUDIT_LOG_USER_NAME_PROPERTY = "user_name";
  162 + public static final String AUDIT_LOG_ACTION_TYPE_PROPERTY = "action_type";
  163 + public static final String AUDIT_LOG_ACTION_DATA_PROPERTY = "action_data";
  164 + public static final String AUDIT_LOG_ACTION_STATUS_PROPERTY = "action_status";
  165 + public static final String AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY = "action_failure_details";
  166 +
  167 + /**
138 168 * Cassandra asset constants.
139 169 */
140 170 public static final String ASSET_COLUMN_FAMILY_NAME = "asset";
... ... @@ -310,13 +340,6 @@ public class ModelConstants {
310 340 public static final String TS_KV_PARTITIONS_CF = "ts_kv_partitions_cf";
311 341 public static final String TS_KV_LATEST_CF = "ts_kv_latest_cf";
312 342
313   -
314   - public static final String ENTITY_TYPE_COLUMN = ENTITY_TYPE_PROPERTY;
315   - public static final String ENTITY_ID_COLUMN = "entity_id";
316   - public static final String ATTRIBUTE_TYPE_COLUMN = "attribute_type";
317   - public static final String ATTRIBUTE_KEY_COLUMN = "attribute_key";
318   - public static final String LAST_UPDATE_TS_COLUMN = "last_update_ts";
319   -
320 343 public static final String PARTITION_COLUMN = "partition";
321 344 public static final String KEY_COLUMN = "key";
322 345 public static final String TS_COLUMN = "ts";
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.nosql;
  17 +
  18 +import com.datastax.driver.mapping.annotations.Column;
  19 +import com.datastax.driver.mapping.annotations.Table;
  20 +import com.fasterxml.jackson.databind.JsonNode;
  21 +import lombok.Data;
  22 +import lombok.NoArgsConstructor;
  23 +import org.thingsboard.server.common.data.EntityType;
  24 +import org.thingsboard.server.common.data.audit.ActionStatus;
  25 +import org.thingsboard.server.common.data.audit.ActionType;
  26 +import org.thingsboard.server.common.data.audit.AuditLog;
  27 +import org.thingsboard.server.common.data.id.*;
  28 +import org.thingsboard.server.dao.model.BaseEntity;
  29 +import org.thingsboard.server.dao.model.type.ActionStatusCodec;
  30 +import org.thingsboard.server.dao.model.type.ActionTypeCodec;
  31 +import org.thingsboard.server.dao.model.type.EntityTypeCodec;
  32 +import org.thingsboard.server.dao.model.type.JsonCodec;
  33 +
  34 +import java.util.UUID;
  35 +
  36 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  37 +
  38 +@Table(name = AUDIT_LOG_COLUMN_FAMILY_NAME)
  39 +@Data
  40 +@NoArgsConstructor
  41 +public class AuditLogEntity implements BaseEntity<AuditLog> {
  42 +
  43 + @Column(name = ID_PROPERTY)
  44 + private UUID id;
  45 +
  46 + @Column(name = AUDIT_LOG_TENANT_ID_PROPERTY)
  47 + private UUID tenantId;
  48 +
  49 + @Column(name = AUDIT_LOG_CUSTOMER_ID_PROPERTY)
  50 + private UUID customerId;
  51 +
  52 + @Column(name = AUDIT_LOG_ENTITY_TYPE_PROPERTY, codec = EntityTypeCodec.class)
  53 + private EntityType entityType;
  54 +
  55 + @Column(name = AUDIT_LOG_ENTITY_ID_PROPERTY)
  56 + private UUID entityId;
  57 +
  58 + @Column(name = AUDIT_LOG_ENTITY_NAME_PROPERTY)
  59 + private String entityName;
  60 +
  61 + @Column(name = AUDIT_LOG_USER_ID_PROPERTY)
  62 + private UUID userId;
  63 +
  64 + @Column(name = AUDIT_LOG_USER_NAME_PROPERTY)
  65 + private String userName;
  66 +
  67 + @Column(name = AUDIT_LOG_ACTION_TYPE_PROPERTY, codec = ActionTypeCodec.class)
  68 + private ActionType actionType;
  69 +
  70 + @Column(name = AUDIT_LOG_ACTION_DATA_PROPERTY, codec = JsonCodec.class)
  71 + private JsonNode actionData;
  72 +
  73 + @Column(name = AUDIT_LOG_ACTION_STATUS_PROPERTY, codec = ActionStatusCodec.class)
  74 + private ActionStatus actionStatus;
  75 +
  76 + @Column(name = AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY)
  77 + private String actionFailureDetails;
  78 +
  79 + @Override
  80 + public UUID getId() {
  81 + return id;
  82 + }
  83 +
  84 + @Override
  85 + public void setId(UUID id) {
  86 + this.id = id;
  87 + }
  88 +
  89 + public AuditLogEntity(AuditLog auditLog) {
  90 + if (auditLog.getId() != null) {
  91 + this.id = auditLog.getId().getId();
  92 + }
  93 + if (auditLog.getTenantId() != null) {
  94 + this.tenantId = auditLog.getTenantId().getId();
  95 + }
  96 + if (auditLog.getEntityId() != null) {
  97 + this.entityType = auditLog.getEntityId().getEntityType();
  98 + this.entityId = auditLog.getEntityId().getId();
  99 + }
  100 + if (auditLog.getCustomerId() != null) {
  101 + this.customerId = auditLog.getCustomerId().getId();
  102 + }
  103 + if (auditLog.getUserId() != null) {
  104 + this.userId = auditLog.getUserId().getId();
  105 + }
  106 + this.entityName = auditLog.getEntityName();
  107 + this.userName = auditLog.getUserName();
  108 + this.actionType = auditLog.getActionType();
  109 + this.actionData = auditLog.getActionData();
  110 + this.actionStatus = auditLog.getActionStatus();
  111 + this.actionFailureDetails = auditLog.getActionFailureDetails();
  112 + }
  113 +
  114 + @Override
  115 + public AuditLog toData() {
  116 + AuditLog auditLog = new AuditLog(new AuditLogId(id));
  117 + if (tenantId != null) {
  118 + auditLog.setTenantId(new TenantId(tenantId));
  119 + }
  120 + if (entityId != null & entityType != null) {
  121 + auditLog.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
  122 + }
  123 + if (customerId != null) {
  124 + auditLog.setCustomerId(new CustomerId(customerId));
  125 + }
  126 + if (userId != null) {
  127 + auditLog.setUserId(new UserId(userId));
  128 + }
  129 + auditLog.setEntityName(this.entityName);
  130 + auditLog.setUserName(this.userName);
  131 + auditLog.setActionType(this.actionType);
  132 + auditLog.setActionData(this.actionData);
  133 + auditLog.setActionStatus(this.actionStatus);
  134 + auditLog.setActionFailureDetails(this.actionFailureDetails);
  135 + return auditLog;
  136 + }
  137 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sql;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import lombok.Data;
  21 +import lombok.EqualsAndHashCode;
  22 +import org.hibernate.annotations.Type;
  23 +import org.hibernate.annotations.TypeDef;
  24 +import org.thingsboard.server.common.data.EntityType;
  25 +import org.thingsboard.server.common.data.audit.ActionStatus;
  26 +import org.thingsboard.server.common.data.audit.ActionType;
  27 +import org.thingsboard.server.common.data.audit.AuditLog;
  28 +import org.thingsboard.server.common.data.id.AuditLogId;
  29 +import org.thingsboard.server.common.data.id.CustomerId;
  30 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  31 +import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.dao.model.BaseEntity;
  33 +import org.thingsboard.server.dao.model.BaseSqlEntity;
  34 +import org.thingsboard.server.dao.model.ModelConstants;
  35 +import org.thingsboard.server.dao.util.mapping.JsonStringType;
  36 +
  37 +import javax.persistence.*;
  38 +
  39 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  40 +
  41 +@Data
  42 +@EqualsAndHashCode(callSuper = true)
  43 +@Entity
  44 +@TypeDef(name = "json", typeClass = JsonStringType.class)
  45 +@Table(name = ModelConstants.AUDIT_LOG_COLUMN_FAMILY_NAME)
  46 +public class AuditLogEntity extends BaseSqlEntity<AuditLog> implements BaseEntity<AuditLog> {
  47 +
  48 + @Column(name = AUDIT_LOG_TENANT_ID_PROPERTY)
  49 + private String tenantId;
  50 +
  51 + @Column(name = AUDIT_LOG_CUSTOMER_ID_PROPERTY)
  52 + private String customerId;
  53 +
  54 + @Enumerated(EnumType.STRING)
  55 + @Column(name = AUDIT_LOG_ENTITY_TYPE_PROPERTY)
  56 + private EntityType entityType;
  57 +
  58 + @Column(name = AUDIT_LOG_ENTITY_ID_PROPERTY)
  59 + private String entityId;
  60 +
  61 + @Column(name = AUDIT_LOG_ENTITY_NAME_PROPERTY)
  62 + private String entityName;
  63 +
  64 + @Column(name = AUDIT_LOG_USER_ID_PROPERTY)
  65 + private String userId;
  66 +
  67 + @Column(name = AUDIT_LOG_USER_NAME_PROPERTY)
  68 + private String userName;
  69 +
  70 + @Enumerated(EnumType.STRING)
  71 + @Column(name = AUDIT_LOG_ACTION_TYPE_PROPERTY)
  72 + private ActionType actionType;
  73 +
  74 + @Type(type = "json")
  75 + @Column(name = AUDIT_LOG_ACTION_DATA_PROPERTY)
  76 + private JsonNode actionData;
  77 +
  78 + @Enumerated(EnumType.STRING)
  79 + @Column(name = AUDIT_LOG_ACTION_STATUS_PROPERTY)
  80 + private ActionStatus actionStatus;
  81 +
  82 + @Column(name = AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY)
  83 + private String actionFailureDetails;
  84 +
  85 + public AuditLogEntity() {
  86 + super();
  87 + }
  88 +
  89 + public AuditLogEntity(AuditLog auditLog) {
  90 + if (auditLog.getId() != null) {
  91 + this.setId(auditLog.getId().getId());
  92 + }
  93 + if (auditLog.getTenantId() != null) {
  94 + this.tenantId = toString(auditLog.getTenantId().getId());
  95 + }
  96 + if (auditLog.getCustomerId() != null) {
  97 + this.customerId = toString(auditLog.getCustomerId().getId());
  98 + }
  99 + if (auditLog.getEntityId() != null) {
  100 + this.entityId = toString(auditLog.getEntityId().getId());
  101 + this.entityType = auditLog.getEntityId().getEntityType();
  102 + }
  103 + this.entityName = auditLog.getEntityName();
  104 + this.userName = auditLog.getUserName();
  105 + this.actionType = auditLog.getActionType();
  106 + this.actionData = auditLog.getActionData();
  107 + this.actionStatus = auditLog.getActionStatus();
  108 + this.actionFailureDetails = auditLog.getActionFailureDetails();
  109 + }
  110 +
  111 + @Override
  112 + public AuditLog toData() {
  113 + AuditLog auditLog = new AuditLog(new AuditLogId(getId()));
  114 + auditLog.setCreatedTime(UUIDs.unixTimestamp(getId()));
  115 + if (tenantId != null) {
  116 + auditLog.setTenantId(new TenantId(toUUID(tenantId)));
  117 + }
  118 + if (customerId != null) {
  119 + auditLog.setCustomerId(new CustomerId(toUUID(customerId)));
  120 + }
  121 + if (entityId != null) {
  122 + auditLog.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), toUUID(entityId).toString()));
  123 + }
  124 + auditLog.setEntityName(this.entityName);
  125 + auditLog.setUserName(this.userName);
  126 + auditLog.setActionType(this.actionType);
  127 + auditLog.setActionData(this.actionData);
  128 + auditLog.setActionStatus(this.actionStatus);
  129 + auditLog.setActionFailureDetails(this.actionFailureDetails);
  130 + return auditLog;
  131 + }
  132 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.type;
  17 +
  18 +import com.datastax.driver.extras.codecs.enums.EnumNameCodec;
  19 +import org.thingsboard.server.common.data.audit.ActionStatus;
  20 +
  21 +public class ActionStatusCodec extends EnumNameCodec<ActionStatus> {
  22 +
  23 + public ActionStatusCodec() {
  24 + super(ActionStatus.class);
  25 + }
  26 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.type;
  17 +
  18 +import com.datastax.driver.extras.codecs.enums.EnumNameCodec;
  19 +import org.thingsboard.server.common.data.audit.ActionType;
  20 +
  21 +public class ActionTypeCodec extends EnumNameCodec<ActionType> {
  22 +
  23 + public ActionTypeCodec() {
  24 + super(ActionType.class);
  25 + }
  26 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.audit;
  17 +
  18 +import org.springframework.data.domain.Pageable;
  19 +import org.springframework.data.jpa.repository.Query;
  20 +import org.springframework.data.repository.CrudRepository;
  21 +import org.springframework.data.repository.query.Param;
  22 +import org.thingsboard.server.common.data.EntityType;
  23 +import org.thingsboard.server.dao.model.sql.AuditLogEntity;
  24 +
  25 +import java.util.List;
  26 +
  27 +public interface AuditLogRepository extends CrudRepository<AuditLogEntity, String> {
  28 +
  29 + @Query("SELECT al FROM AuditLogEntity al WHERE al.tenantId = :tenantId " +
  30 + "AND al.id > :idOffset ORDER BY al.id")
  31 + List<AuditLogEntity> findByTenantId(@Param("tenantId") String tenantId,
  32 + @Param("idOffset") String idOffset,
  33 + Pageable pageable);
  34 +
  35 + @Query("SELECT al FROM AuditLogEntity al WHERE al.tenantId = :tenantId " +
  36 + "AND al.entityType = :entityType " +
  37 + "AND al.entityId = :entityId " +
  38 + "AND al.id > :idOffset ORDER BY al.id")
  39 + List<AuditLogEntity> findByTenantIdAndEntityId(@Param("tenantId") String tenantId,
  40 + @Param("entityId") String entityId,
  41 + @Param("entityType") EntityType entityType,
  42 + @Param("idOffset") String idOffset,
  43 + Pageable pageable);
  44 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.audit;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import com.google.common.util.concurrent.ListeningExecutorService;
  20 +import com.google.common.util.concurrent.MoreExecutors;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.data.domain.PageRequest;
  23 +import org.springframework.data.repository.CrudRepository;
  24 +import org.springframework.stereotype.Component;
  25 +import org.thingsboard.server.common.data.audit.AuditLog;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.page.TimePageLink;
  28 +import org.thingsboard.server.dao.DaoUtil;
  29 +import org.thingsboard.server.dao.audit.AuditLogDao;
  30 +import org.thingsboard.server.dao.model.sql.AuditLogEntity;
  31 +import org.thingsboard.server.dao.sql.JpaAbstractDao;
  32 +import org.thingsboard.server.dao.util.SqlDao;
  33 +
  34 +import javax.annotation.PreDestroy;
  35 +import java.util.List;
  36 +import java.util.UUID;
  37 +import java.util.concurrent.Executors;
  38 +
  39 +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
  40 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR;
  41 +
  42 +@Component
  43 +@SqlDao
  44 +public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> implements AuditLogDao {
  45 +
  46 + private ListeningExecutorService insertService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
  47 +
  48 + @Autowired
  49 + private AuditLogRepository auditLogRepository;
  50 +
  51 + @Override
  52 + protected Class<AuditLogEntity> getEntityClass() {
  53 + return AuditLogEntity.class;
  54 + }
  55 +
  56 + @Override
  57 + protected CrudRepository<AuditLogEntity, String> getCrudRepository() {
  58 + return auditLogRepository;
  59 + }
  60 +
  61 + @PreDestroy
  62 + void onDestroy() {
  63 + insertService.shutdown();
  64 + }
  65 +
  66 + @Override
  67 + public ListenableFuture<Void> saveByTenantId(AuditLog auditLog) {
  68 + return insertService.submit(() -> {
  69 + save(auditLog);
  70 + return null;
  71 + });
  72 + }
  73 +
  74 + @Override
  75 + public ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog) {
  76 + return insertService.submit(() -> null);
  77 + }
  78 +
  79 + @Override
  80 + public ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog) {
  81 + return insertService.submit(() -> null);
  82 + }
  83 +
  84 + @Override
  85 + public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) {
  86 + return DaoUtil.convertDataList(
  87 + auditLogRepository.findByTenantIdAndEntityId(
  88 + fromTimeUUID(tenantId),
  89 + fromTimeUUID(entityId.getId()),
  90 + entityId.getEntityType(),
  91 + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
  92 + new PageRequest(0, pageLink.getLimit())));
  93 + }
  94 +
  95 + @Override
  96 + public List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink) {
  97 + return DaoUtil.convertDataList(
  98 + auditLogRepository.findByTenantId(
  99 + fromTimeUUID(tenantId),
  100 + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
  101 + new PageRequest(0, pageLink.getLimit())));
  102 + }
  103 +}
... ...
... ... @@ -548,3 +548,44 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.event_by_id AS
548 548 AND event_type IS NOT NULL AND event_uid IS NOT NULL
549 549 PRIMARY KEY ((tenant_id, entity_type, entity_id), id, event_type, event_uid)
550 550 WITH CLUSTERING ORDER BY (id ASC, event_type ASC, event_uid ASC);
  551 +
  552 +
  553 +CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id (
  554 + tenant_id timeuuid,
  555 + id timeuuid,
  556 + customer_id timeuuid,
  557 + entity_id timeuuid,
  558 + entity_type text,
  559 + entity_name text,
  560 + user_id timeuuid,
  561 + user_name text,
  562 + action_type text,
  563 + action_data text,
  564 + action_status text,
  565 + action_failure_details text,
  566 + PRIMARY KEY ((tenant_id, entity_id, entity_type), id)
  567 +);
  568 +
  569 +CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id (
  570 + tenant_id timeuuid,
  571 + id timeuuid,
  572 + partition bigint,
  573 + customer_id timeuuid,
  574 + entity_id timeuuid,
  575 + entity_type text,
  576 + entity_name text,
  577 + user_id timeuuid,
  578 + user_name text,
  579 + action_type text,
  580 + action_data text,
  581 + action_status text,
  582 + action_failure_details text,
  583 + PRIMARY KEY ((tenant_id, partition), id)
  584 +);
  585 +
  586 +CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions (
  587 + tenant_id timeuuid,
  588 + partition bigint,
  589 + PRIMARY KEY (( tenant_id ), partition)
  590 +) WITH CLUSTERING ORDER BY ( partition ASC )
  591 +AND compaction = { 'class' : 'LeveledCompactionStrategy' };
\ No newline at end of file
... ...
... ... @@ -47,6 +47,22 @@ CREATE TABLE IF NOT EXISTS asset (
47 47 type varchar(255)
48 48 );
49 49
  50 +CREATE TABLE IF NOT EXISTS audit_log (
  51 + id varchar(31) NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY,
  52 + tenant_id varchar(31),
  53 + customer_id varchar(31),
  54 + entity_id varchar(31),
  55 + entity_type varchar(255),
  56 + entity_name varchar(255),
  57 + user_id varchar(31),
  58 + user_name varchar(255),
  59 + action_type varchar(255),
  60 + action_data varchar(255),
  61 + action_status varchar(255),
  62 + search_text varchar(255),
  63 + action_failure_details varchar
  64 +);
  65 +
50 66 CREATE TABLE IF NOT EXISTS attribute_kv (
51 67 entity_type varchar(255),
52 68 entity_id varchar(31),
... ...
... ... @@ -24,7 +24,7 @@ import java.util.Arrays;
24 24
25 25 @RunWith(ClasspathSuite.class)
26 26 @ClassnameFilters({
27   - "org.thingsboard.server.dao.sql.*AAATest"
  27 + "org.thingsboard.server.dao.sql.*THIS_MUST_BE_FIXED_Test"
28 28 })
29 29 public class JpaDaoTestSuite {
30 30
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.audit;
  17 +
  18 +import org.thingsboard.server.dao.AbstractJpaDaoTest;
  19 +
  20 +public class JpaAuditLogDaoTest extends AbstractJpaDaoTest {
  21 +}
... ...
1 1 DROP TABLE IF EXISTS admin_settings;
2 2 DROP TABLE IF EXISTS alarm;
3 3 DROP TABLE IF EXISTS asset;
  4 +DROP TABLE IF EXISTS audit_log;
4 5 DROP TABLE IF EXISTS attribute_kv;
5 6 DROP TABLE IF EXISTS component_descriptor;
6 7 DROP TABLE IF EXISTS customer;
... ...