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 | } | ... | ... |
application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java
0 → 100644
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 | +} | ... | ... |
application/src/test/java/org/thingsboard/server/controller/nosql/AuditLogControllerNoSqlTest.java
0 → 100644
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 | +} | ... | ... |
application/src/test/java/org/thingsboard/server/controller/sql/AuditLogControllerSqlTest.java
0 → 100644
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), | ... | ... |
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 | +} | ... | ... |