27 changed files with 1472 additions and 11 deletions
@ -0,0 +1,70 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.controller; |
|||
|
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.TimePageData; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
import org.thingsboard.server.exception.ThingsboardException; |
|||
|
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class AuditLogController extends BaseController { |
|||
|
|||
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
|||
@RequestMapping(value = "/audit/logs/{entityType}/{entityId}", params = {"limit"}, method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public TimePageData<AuditLog> getAuditLogs( |
|||
@PathVariable("entityType") String strEntityType, |
|||
@PathVariable("entityId") String strEntityId, |
|||
@RequestParam int limit, |
|||
@RequestParam(required = false) Long startTime, |
|||
@RequestParam(required = false) Long endTime, |
|||
@RequestParam(required = false, defaultValue = "false") boolean ascOrder, |
|||
@RequestParam(required = false) String offset) throws ThingsboardException { |
|||
try { |
|||
checkParameter("EntityId", strEntityId); |
|||
checkParameter("EntityType", strEntityType); |
|||
TenantId tenantId = getCurrentUser().getTenantId(); |
|||
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); |
|||
return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), pageLink)); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
|||
@RequestMapping(value = "/audit/logs", params = {"limit"}, method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public TimePageData<AuditLog> getAuditLogs( |
|||
@RequestParam int limit, |
|||
@RequestParam(required = false) Long startTime, |
|||
@RequestParam(required = false) Long endTime, |
|||
@RequestParam(required = false, defaultValue = "false") boolean ascOrder, |
|||
@RequestParam(required = false) String offset) throws ThingsboardException { |
|||
try { |
|||
TenantId tenantId = getCurrentUser().getTenantId(); |
|||
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); |
|||
return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, pageLink)); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,118 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.controller; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.page.TimePageData; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
public abstract class BaseAuditLogControllerTest extends AbstractControllerTest { |
|||
|
|||
private Tenant savedTenant; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
Tenant tenant = new Tenant(); |
|||
tenant.setTitle("My tenant"); |
|||
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
|||
Assert.assertNotNull(savedTenant); |
|||
|
|||
User tenantAdmin = new User(); |
|||
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
|||
tenantAdmin.setTenantId(savedTenant.getId()); |
|||
tenantAdmin.setEmail("tenant2@thingsboard.org"); |
|||
tenantAdmin.setFirstName("Joe"); |
|||
tenantAdmin.setLastName("Downs"); |
|||
|
|||
createUserAndLogin(tenantAdmin, "testPassword1"); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testSaveDeviceAuditLogs() throws Exception { |
|||
for (int i = 0; i < 178; i++) { |
|||
Device device = new Device(); |
|||
device.setName("Device" + i); |
|||
device.setType("default"); |
|||
doPost("/api/device", device, Device.class); |
|||
} |
|||
|
|||
List<AuditLog> loadedAuditLogs = new ArrayList<>(); |
|||
TimePageLink pageLink = new TimePageLink(23); |
|||
TimePageData<AuditLog> pageData; |
|||
do { |
|||
pageData = doGetTypedWithTimePageLink("/api/audit/logs?", |
|||
new TypeReference<TimePageData<AuditLog>>() { |
|||
}, pageLink); |
|||
loadedAuditLogs.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageData.getNextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Assert.assertEquals(178, loadedAuditLogs.size()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateDeviceAuditLogs() throws Exception { |
|||
Device device = new Device(); |
|||
device.setName("Device name"); |
|||
device.setType("default"); |
|||
Device savedDevice = doPost("/api/device", device, Device.class); |
|||
for (int i = 0; i < 178; i++) { |
|||
savedDevice.setName("Device name" + i); |
|||
doPost("/api/device", savedDevice, Device.class); |
|||
} |
|||
|
|||
List<AuditLog> loadedAuditLogs = new ArrayList<>(); |
|||
TimePageLink pageLink = new TimePageLink(23); |
|||
TimePageData<AuditLog> pageData; |
|||
do { |
|||
pageData = doGetTypedWithTimePageLink("/api/audit/logs/DEVICE/" + savedDevice.getId().getId() + "?", |
|||
new TypeReference<TimePageData<AuditLog>>() { |
|||
}, pageLink); |
|||
loadedAuditLogs.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageData.getNextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Assert.assertEquals(179, loadedAuditLogs.size()); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.controller.nosql; |
|||
|
|||
import org.thingsboard.server.controller.BaseAuditLogControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
|
|||
@DaoNoSqlTest |
|||
public class AuditLogControllerNoSqlTest extends BaseAuditLogControllerTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.controller.sql; |
|||
|
|||
import org.thingsboard.server.controller.BaseAuditLogControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
@DaoSqlTest |
|||
public class AuditLogControllerSqlTest extends BaseAuditLogControllerTest { |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.audit; |
|||
|
|||
public enum ActionStatus { |
|||
SUCCESS, FAILURE |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.audit; |
|||
|
|||
public enum ActionType { |
|||
ADDED, DELETED, UPDATED, ATTRIBUTE_UPDATED, ATTRIBUTE_DELETED, ATTRIBUTE_ADDED, RPC_CALL, CREDENTIALS_UPDATED |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.audit; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.BaseData; |
|||
import org.thingsboard.server.common.data.id.*; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
public class AuditLog extends BaseData<AuditLogId> { |
|||
|
|||
private TenantId tenantId; |
|||
private CustomerId customerId; |
|||
private EntityId entityId; |
|||
private String entityName; |
|||
private UserId userId; |
|||
private String userName; |
|||
private ActionType actionType; |
|||
private JsonNode actionData; |
|||
private ActionStatus actionStatus; |
|||
private String actionFailureDetails; |
|||
|
|||
public AuditLog() { |
|||
super(); |
|||
} |
|||
|
|||
public AuditLog(AuditLogId id) { |
|||
super(id); |
|||
} |
|||
|
|||
public AuditLog(AuditLog auditLog) { |
|||
super(auditLog); |
|||
this.tenantId = auditLog.getTenantId(); |
|||
this.customerId = auditLog.getCustomerId(); |
|||
this.entityId = auditLog.getEntityId(); |
|||
this.entityName = auditLog.getEntityName(); |
|||
this.userId = auditLog.getUserId(); |
|||
this.userName = auditLog.getUserName(); |
|||
this.actionType = auditLog.getActionType(); |
|||
this.actionData = auditLog.getActionData(); |
|||
this.actionStatus = auditLog.getActionStatus(); |
|||
this.actionFailureDetails = auditLog.getActionFailureDetails(); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.id; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonCreator; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public class AuditLogId extends UUIDBased { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@JsonCreator |
|||
public AuditLogId(@JsonProperty("id") UUID id) { |
|||
super(id); |
|||
} |
|||
|
|||
public static AuditLogId fromString(String auditLogId) { |
|||
return new AuditLogId(UUID.fromString(auditLogId)); |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.audit; |
|||
|
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
|
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
|
|||
public interface AuditLogDao { |
|||
|
|||
ListenableFuture<Void> saveByTenantId(AuditLog auditLog); |
|||
|
|||
ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog); |
|||
|
|||
ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog); |
|||
|
|||
List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink); |
|||
|
|||
List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink); |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.audit; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import org.thingsboard.server.common.data.audit.ActionStatus; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.id.UserId; |
|||
import org.thingsboard.server.common.data.page.TimePageData; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface AuditLogService { |
|||
|
|||
TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink); |
|||
|
|||
TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink); |
|||
|
|||
ListenableFuture<List<Void>> logAction(TenantId tenantId, |
|||
EntityId entityId, |
|||
String entityName, |
|||
CustomerId customerId, |
|||
UserId userId, |
|||
String userName, |
|||
ActionType actionType, |
|||
JsonNode actionData, |
|||
ActionStatus actionStatus, |
|||
String actionFailureDetails); |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.audit; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.google.common.collect.Lists; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.audit.ActionStatus; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.id.UserId; |
|||
import org.thingsboard.server.common.data.page.TimePageData; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
import java.util.List; |
|||
|
|||
import static org.thingsboard.server.dao.service.Validator.validateEntityId; |
|||
import static org.thingsboard.server.dao.service.Validator.validateId; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class AuditLogServiceImpl implements AuditLogService { |
|||
|
|||
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; |
|||
private static final int INSERTS_PER_ENTRY = 3; |
|||
|
|||
@Autowired |
|||
private AuditLogDao auditLogDao; |
|||
|
|||
@Override |
|||
public TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, TimePageLink pageLink) { |
|||
log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink); |
|||
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
|||
validateEntityId(entityId, INCORRECT_TENANT_ID + entityId); |
|||
List<AuditLog> auditLogs = auditLogDao.findAuditLogsByTenantIdAndEntityId(tenantId.getId(), entityId, pageLink); |
|||
return new TimePageData<>(auditLogs, pageLink); |
|||
} |
|||
|
|||
@Override |
|||
public TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink) { |
|||
log.trace("Executing findAuditLogs [{}]", pageLink); |
|||
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
|||
List<AuditLog> auditLogs = auditLogDao.findAuditLogsByTenantId(tenantId.getId(), pageLink); |
|||
return new TimePageData<>(auditLogs, pageLink); |
|||
} |
|||
|
|||
private AuditLog createAuditLogEntry(TenantId tenantId, |
|||
EntityId entityId, |
|||
String entityName, |
|||
CustomerId customerId, |
|||
UserId userId, |
|||
String userName, |
|||
ActionType actionType, |
|||
JsonNode actionData, |
|||
ActionStatus actionStatus, |
|||
String actionFailureDetails) { |
|||
AuditLog result = new AuditLog(); |
|||
result.setTenantId(tenantId); |
|||
result.setEntityId(entityId); |
|||
result.setEntityName(entityName); |
|||
result.setCustomerId(customerId); |
|||
result.setUserId(userId); |
|||
result.setUserName(userName); |
|||
result.setActionType(actionType); |
|||
result.setActionData(actionData); |
|||
result.setActionStatus(actionStatus); |
|||
result.setActionFailureDetails(actionFailureDetails); |
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<List<Void>> logAction(TenantId tenantId, |
|||
EntityId entityId, |
|||
String entityName, |
|||
CustomerId customerId, |
|||
UserId userId, |
|||
String userName, |
|||
ActionType actionType, |
|||
JsonNode actionData, |
|||
ActionStatus actionStatus, |
|||
String actionFailureDetails) { |
|||
AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName, |
|||
actionType, actionData, actionStatus, actionFailureDetails); |
|||
log.trace("Executing logAction [{}]", auditLogEntry); |
|||
auditLogValidator.validate(auditLogEntry); |
|||
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY); |
|||
futures.add(auditLogDao.savePartitionsByTenantId(auditLogEntry)); |
|||
futures.add(auditLogDao.saveByTenantId(auditLogEntry)); |
|||
futures.add(auditLogDao.saveByTenantIdAndEntityId(auditLogEntry)); |
|||
return Futures.allAsList(futures); |
|||
} |
|||
|
|||
private DataValidator<AuditLog> auditLogValidator = |
|||
new DataValidator<AuditLog>() { |
|||
@Override |
|||
protected void validateDataImpl(AuditLog auditLog) { |
|||
if (auditLog.getEntityId() == null) { |
|||
throw new DataValidationException("Entity Id should be specified!"); |
|||
} |
|||
if (auditLog.getTenantId() == null) { |
|||
throw new DataValidationException("Tenant Id should be specified!"); |
|||
} |
|||
if (auditLog.getUserId() == null) { |
|||
throw new DataValidationException("User Id should be specified!"); |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
@ -0,0 +1,242 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.audit; |
|||
|
|||
import com.datastax.driver.core.BoundStatement; |
|||
import com.datastax.driver.core.PreparedStatement; |
|||
import com.datastax.driver.core.ResultSet; |
|||
import com.datastax.driver.core.ResultSetFuture; |
|||
import com.datastax.driver.core.utils.UUIDs; |
|||
import com.google.common.base.Function; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.core.env.Environment; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.AuditLogId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
import org.thingsboard.server.dao.DaoUtil; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.model.nosql.AuditLogEntity; |
|||
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTimeDao; |
|||
import org.thingsboard.server.dao.timeseries.TsPartitionDate; |
|||
import org.thingsboard.server.dao.util.NoSqlDao; |
|||
|
|||
import javax.annotation.Nullable; |
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
import java.time.Instant; |
|||
import java.time.LocalDate; |
|||
import java.time.LocalDateTime; |
|||
import java.time.ZoneOffset; |
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.*; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
@NoSqlDao |
|||
public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLogEntity, AuditLog> implements AuditLogDao { |
|||
|
|||
private static final String INSERT_INTO = "INSERT INTO "; |
|||
|
|||
@Autowired |
|||
private Environment environment; |
|||
|
|||
@Override |
|||
protected Class<AuditLogEntity> getColumnFamilyClass() { |
|||
return AuditLogEntity.class; |
|||
} |
|||
|
|||
@Override |
|||
protected String getColumnFamilyName() { |
|||
return AUDIT_LOG_COLUMN_FAMILY_NAME; |
|||
} |
|||
|
|||
protected ExecutorService readResultsProcessingExecutor; |
|||
|
|||
@Value("${cassandra.query.ts_key_value_partitioning}") |
|||
private String partitioning; |
|||
private TsPartitionDate tsFormat; |
|||
|
|||
private PreparedStatement[] saveStmts; |
|||
|
|||
private boolean isInstall() { |
|||
return environment.acceptsProfiles("install"); |
|||
} |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (!isInstall()) { |
|||
Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning); |
|||
if (partition.isPresent()) { |
|||
tsFormat = partition.get(); |
|||
} else { |
|||
log.warn("Incorrect configuration of partitioning {}", partitioning); |
|||
throw new RuntimeException("Failed to parse partitioning property: " + partitioning + "!"); |
|||
} |
|||
} |
|||
readResultsProcessingExecutor = Executors.newCachedThreadPool(); |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void stopExecutor() { |
|||
if (readResultsProcessingExecutor != null) { |
|||
readResultsProcessingExecutor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
private <T> ListenableFuture<T> getFuture(ResultSetFuture future, java.util.function.Function<ResultSet, T> transformer) { |
|||
return Futures.transform(future, new Function<ResultSet, T>() { |
|||
@Nullable |
|||
@Override |
|||
public T apply(@Nullable ResultSet input) { |
|||
return transformer.apply(input); |
|||
} |
|||
}, readResultsProcessingExecutor); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> saveByTenantId(AuditLog auditLog) { |
|||
log.debug("Save saveByTenantId [{}] ", auditLog); |
|||
|
|||
AuditLogId auditLogId = new AuditLogId(UUIDs.timeBased()); |
|||
|
|||
long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli()); |
|||
BoundStatement stmt = getSaveByTenantStmt().bind(); |
|||
stmt.setUUID(0, auditLogId.getId()) |
|||
.setUUID(1, auditLog.getTenantId().getId()) |
|||
.setUUID(2, auditLog.getEntityId().getId()) |
|||
.setString(3, auditLog.getEntityId().getEntityType().name()) |
|||
.setString(4, auditLog.getActionType().name()) |
|||
.setLong(5, partition); |
|||
return getFuture(executeAsyncWrite(stmt), rs -> null); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog) { |
|||
log.debug("Save saveByTenantIdAndEntityId [{}] ", auditLog); |
|||
|
|||
AuditLogId auditLogId = new AuditLogId(UUIDs.timeBased()); |
|||
|
|||
BoundStatement stmt = getSaveByTenantIdAndEntityIdStmt().bind(); |
|||
stmt.setUUID(0, auditLogId.getId()) |
|||
.setUUID(1, auditLog.getTenantId().getId()) |
|||
.setUUID(2, auditLog.getEntityId().getId()) |
|||
.setString(3, auditLog.getEntityId().getEntityType().name()) |
|||
.setString(4, auditLog.getActionType().name()); |
|||
return getFuture(executeAsyncWrite(stmt), rs -> null); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog) { |
|||
log.debug("Save savePartitionsByTenantId [{}] ", auditLog); |
|||
|
|||
long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli()); |
|||
|
|||
BoundStatement stmt = getPartitionInsertStmt().bind(); |
|||
stmt = stmt.setUUID(0, auditLog.getTenantId().getId()) |
|||
.setLong(1, partition); |
|||
return getFuture(executeAsyncWrite(stmt), rs -> null); |
|||
} |
|||
|
|||
private PreparedStatement getPartitionInsertStmt() { |
|||
// TODO: ADD CACHE LOGIC
|
|||
return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_PARTITIONS_CF + |
|||
"(" + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" + |
|||
" VALUES(?, ?)"); |
|||
} |
|||
|
|||
private PreparedStatement getSaveByTenantIdAndEntityIdStmt() { |
|||
// TODO: ADD CACHE LOGIC
|
|||
return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_ENTITY_ID_CF + |
|||
"(" + ModelConstants.AUDIT_LOG_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY + ")" + |
|||
" VALUES(?, ?, ?, ?, ?)"); |
|||
} |
|||
|
|||
private PreparedStatement getSaveByTenantStmt() { |
|||
// TODO: ADD CACHE LOGIC
|
|||
return getSession().prepare(INSERT_INTO + ModelConstants.AUDIT_LOG_BY_TENANT_ID_CF + |
|||
"(" + ModelConstants.AUDIT_LOG_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY + |
|||
"," + ModelConstants.AUDIT_LOG_PARTITION_PROPERTY + ")" + |
|||
" VALUES(?, ?, ?, ?, ?, ?)"); |
|||
} |
|||
|
|||
private long toPartitionTs(long ts) { |
|||
LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts), ZoneOffset.UTC); |
|||
return tsFormat.truncatedTo(time).toInstant(ZoneOffset.UTC).toEpochMilli(); |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) { |
|||
log.trace("Try to find audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink); |
|||
List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_ENTITY_ID_CF, |
|||
Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId), |
|||
eq(ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY, entityId.getEntityType()), |
|||
eq(ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY, entityId.getId())), |
|||
pageLink); |
|||
log.trace("Found audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink); |
|||
return DaoUtil.convertDataList(entities); |
|||
} |
|||
|
|||
@Override |
|||
public List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink) { |
|||
log.trace("Try to find audit logs by tenant [{}] and pageLink [{}]", tenantId, pageLink); |
|||
|
|||
// TODO: ADD AUDIT LOG PARTITION CURSOR LOGIC
|
|||
|
|||
long minPartition; |
|||
long maxPartition; |
|||
|
|||
if (pageLink.getStartTime() != null && pageLink.getStartTime() != 0) { |
|||
minPartition = toPartitionTs(pageLink.getStartTime()); |
|||
} else { |
|||
minPartition = toPartitionTs(LocalDate.now().minusMonths(1).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli()); |
|||
} |
|||
|
|||
if (pageLink.getEndTime() != null && pageLink.getEndTime() != 0) { |
|||
maxPartition = toPartitionTs(pageLink.getEndTime()); |
|||
} else { |
|||
maxPartition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli()); |
|||
} |
|||
List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_TENANT_ID_CF, |
|||
Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId), |
|||
eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, maxPartition)), |
|||
pageLink); |
|||
log.trace("Found audit logs by tenant [{}] and pageLink [{}]", tenantId, pageLink); |
|||
return DaoUtil.convertDataList(entities); |
|||
} |
|||
} |
|||
@ -0,0 +1,137 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.nosql; |
|||
|
|||
import com.datastax.driver.mapping.annotations.Column; |
|||
import com.datastax.driver.mapping.annotations.Table; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.audit.ActionStatus; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.*; |
|||
import org.thingsboard.server.dao.model.BaseEntity; |
|||
import org.thingsboard.server.dao.model.type.ActionStatusCodec; |
|||
import org.thingsboard.server.dao.model.type.ActionTypeCodec; |
|||
import org.thingsboard.server.dao.model.type.EntityTypeCodec; |
|||
import org.thingsboard.server.dao.model.type.JsonCodec; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.*; |
|||
|
|||
@Table(name = AUDIT_LOG_COLUMN_FAMILY_NAME) |
|||
@Data |
|||
@NoArgsConstructor |
|||
public class AuditLogEntity implements BaseEntity<AuditLog> { |
|||
|
|||
@Column(name = ID_PROPERTY) |
|||
private UUID id; |
|||
|
|||
@Column(name = AUDIT_LOG_TENANT_ID_PROPERTY) |
|||
private UUID tenantId; |
|||
|
|||
@Column(name = AUDIT_LOG_CUSTOMER_ID_PROPERTY) |
|||
private UUID customerId; |
|||
|
|||
@Column(name = AUDIT_LOG_ENTITY_TYPE_PROPERTY, codec = EntityTypeCodec.class) |
|||
private EntityType entityType; |
|||
|
|||
@Column(name = AUDIT_LOG_ENTITY_ID_PROPERTY) |
|||
private UUID entityId; |
|||
|
|||
@Column(name = AUDIT_LOG_ENTITY_NAME_PROPERTY) |
|||
private String entityName; |
|||
|
|||
@Column(name = AUDIT_LOG_USER_ID_PROPERTY) |
|||
private UUID userId; |
|||
|
|||
@Column(name = AUDIT_LOG_USER_NAME_PROPERTY) |
|||
private String userName; |
|||
|
|||
@Column(name = AUDIT_LOG_ACTION_TYPE_PROPERTY, codec = ActionTypeCodec.class) |
|||
private ActionType actionType; |
|||
|
|||
@Column(name = AUDIT_LOG_ACTION_DATA_PROPERTY, codec = JsonCodec.class) |
|||
private JsonNode actionData; |
|||
|
|||
@Column(name = AUDIT_LOG_ACTION_STATUS_PROPERTY, codec = ActionStatusCodec.class) |
|||
private ActionStatus actionStatus; |
|||
|
|||
@Column(name = AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY) |
|||
private String actionFailureDetails; |
|||
|
|||
@Override |
|||
public UUID getId() { |
|||
return id; |
|||
} |
|||
|
|||
@Override |
|||
public void setId(UUID id) { |
|||
this.id = id; |
|||
} |
|||
|
|||
public AuditLogEntity(AuditLog auditLog) { |
|||
if (auditLog.getId() != null) { |
|||
this.id = auditLog.getId().getId(); |
|||
} |
|||
if (auditLog.getTenantId() != null) { |
|||
this.tenantId = auditLog.getTenantId().getId(); |
|||
} |
|||
if (auditLog.getEntityId() != null) { |
|||
this.entityType = auditLog.getEntityId().getEntityType(); |
|||
this.entityId = auditLog.getEntityId().getId(); |
|||
} |
|||
if (auditLog.getCustomerId() != null) { |
|||
this.customerId = auditLog.getCustomerId().getId(); |
|||
} |
|||
if (auditLog.getUserId() != null) { |
|||
this.userId = auditLog.getUserId().getId(); |
|||
} |
|||
this.entityName = auditLog.getEntityName(); |
|||
this.userName = auditLog.getUserName(); |
|||
this.actionType = auditLog.getActionType(); |
|||
this.actionData = auditLog.getActionData(); |
|||
this.actionStatus = auditLog.getActionStatus(); |
|||
this.actionFailureDetails = auditLog.getActionFailureDetails(); |
|||
} |
|||
|
|||
@Override |
|||
public AuditLog toData() { |
|||
AuditLog auditLog = new AuditLog(new AuditLogId(id)); |
|||
if (tenantId != null) { |
|||
auditLog.setTenantId(new TenantId(tenantId)); |
|||
} |
|||
if (entityId != null & entityType != null) { |
|||
auditLog.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); |
|||
} |
|||
if (customerId != null) { |
|||
auditLog.setCustomerId(new CustomerId(customerId)); |
|||
} |
|||
if (userId != null) { |
|||
auditLog.setUserId(new UserId(userId)); |
|||
} |
|||
auditLog.setEntityName(this.entityName); |
|||
auditLog.setUserName(this.userName); |
|||
auditLog.setActionType(this.actionType); |
|||
auditLog.setActionData(this.actionData); |
|||
auditLog.setActionStatus(this.actionStatus); |
|||
auditLog.setActionFailureDetails(this.actionFailureDetails); |
|||
return auditLog; |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import com.datastax.driver.core.utils.UUIDs; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.Type; |
|||
import org.hibernate.annotations.TypeDef; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.audit.ActionStatus; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.AuditLogId; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.model.BaseEntity; |
|||
import org.thingsboard.server.dao.model.BaseSqlEntity; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
|||
|
|||
import javax.persistence.*; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.*; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Entity |
|||
@TypeDef(name = "json", typeClass = JsonStringType.class) |
|||
@Table(name = ModelConstants.AUDIT_LOG_COLUMN_FAMILY_NAME) |
|||
public class AuditLogEntity extends BaseSqlEntity<AuditLog> implements BaseEntity<AuditLog> { |
|||
|
|||
@Column(name = AUDIT_LOG_TENANT_ID_PROPERTY) |
|||
private String tenantId; |
|||
|
|||
@Column(name = AUDIT_LOG_CUSTOMER_ID_PROPERTY) |
|||
private String customerId; |
|||
|
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = AUDIT_LOG_ENTITY_TYPE_PROPERTY) |
|||
private EntityType entityType; |
|||
|
|||
@Column(name = AUDIT_LOG_ENTITY_ID_PROPERTY) |
|||
private String entityId; |
|||
|
|||
@Column(name = AUDIT_LOG_ENTITY_NAME_PROPERTY) |
|||
private String entityName; |
|||
|
|||
@Column(name = AUDIT_LOG_USER_ID_PROPERTY) |
|||
private String userId; |
|||
|
|||
@Column(name = AUDIT_LOG_USER_NAME_PROPERTY) |
|||
private String userName; |
|||
|
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = AUDIT_LOG_ACTION_TYPE_PROPERTY) |
|||
private ActionType actionType; |
|||
|
|||
@Type(type = "json") |
|||
@Column(name = AUDIT_LOG_ACTION_DATA_PROPERTY) |
|||
private JsonNode actionData; |
|||
|
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = AUDIT_LOG_ACTION_STATUS_PROPERTY) |
|||
private ActionStatus actionStatus; |
|||
|
|||
@Column(name = AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY) |
|||
private String actionFailureDetails; |
|||
|
|||
public AuditLogEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public AuditLogEntity(AuditLog auditLog) { |
|||
if (auditLog.getId() != null) { |
|||
this.setId(auditLog.getId().getId()); |
|||
} |
|||
if (auditLog.getTenantId() != null) { |
|||
this.tenantId = toString(auditLog.getTenantId().getId()); |
|||
} |
|||
if (auditLog.getCustomerId() != null) { |
|||
this.customerId = toString(auditLog.getCustomerId().getId()); |
|||
} |
|||
if (auditLog.getEntityId() != null) { |
|||
this.entityId = toString(auditLog.getEntityId().getId()); |
|||
this.entityType = auditLog.getEntityId().getEntityType(); |
|||
} |
|||
this.entityName = auditLog.getEntityName(); |
|||
this.userName = auditLog.getUserName(); |
|||
this.actionType = auditLog.getActionType(); |
|||
this.actionData = auditLog.getActionData(); |
|||
this.actionStatus = auditLog.getActionStatus(); |
|||
this.actionFailureDetails = auditLog.getActionFailureDetails(); |
|||
} |
|||
|
|||
@Override |
|||
public AuditLog toData() { |
|||
AuditLog auditLog = new AuditLog(new AuditLogId(getId())); |
|||
auditLog.setCreatedTime(UUIDs.unixTimestamp(getId())); |
|||
if (tenantId != null) { |
|||
auditLog.setTenantId(new TenantId(toUUID(tenantId))); |
|||
} |
|||
if (customerId != null) { |
|||
auditLog.setCustomerId(new CustomerId(toUUID(customerId))); |
|||
} |
|||
if (entityId != null) { |
|||
auditLog.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), toUUID(entityId).toString())); |
|||
} |
|||
auditLog.setEntityName(this.entityName); |
|||
auditLog.setUserName(this.userName); |
|||
auditLog.setActionType(this.actionType); |
|||
auditLog.setActionData(this.actionData); |
|||
auditLog.setActionStatus(this.actionStatus); |
|||
auditLog.setActionFailureDetails(this.actionFailureDetails); |
|||
return auditLog; |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.type; |
|||
|
|||
import com.datastax.driver.extras.codecs.enums.EnumNameCodec; |
|||
import org.thingsboard.server.common.data.audit.ActionStatus; |
|||
|
|||
public class ActionStatusCodec extends EnumNameCodec<ActionStatus> { |
|||
|
|||
public ActionStatusCodec() { |
|||
super(ActionStatus.class); |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.type; |
|||
|
|||
import com.datastax.driver.extras.codecs.enums.EnumNameCodec; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
|
|||
public class ActionTypeCodec extends EnumNameCodec<ActionType> { |
|||
|
|||
public ActionTypeCodec() { |
|||
super(ActionType.class); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.audit; |
|||
|
|||
import org.springframework.data.domain.Pageable; |
|||
import org.springframework.data.jpa.repository.Query; |
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.springframework.data.repository.query.Param; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.dao.model.sql.AuditLogEntity; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface AuditLogRepository extends CrudRepository<AuditLogEntity, String> { |
|||
|
|||
@Query("SELECT al FROM AuditLogEntity al WHERE al.tenantId = :tenantId " + |
|||
"AND al.id > :idOffset ORDER BY al.id") |
|||
List<AuditLogEntity> findByTenantId(@Param("tenantId") String tenantId, |
|||
@Param("idOffset") String idOffset, |
|||
Pageable pageable); |
|||
|
|||
@Query("SELECT al FROM AuditLogEntity al WHERE al.tenantId = :tenantId " + |
|||
"AND al.entityType = :entityType " + |
|||
"AND al.entityId = :entityId " + |
|||
"AND al.id > :idOffset ORDER BY al.id") |
|||
List<AuditLogEntity> findByTenantIdAndEntityId(@Param("tenantId") String tenantId, |
|||
@Param("entityId") String entityId, |
|||
@Param("entityType") EntityType entityType, |
|||
@Param("idOffset") String idOffset, |
|||
Pageable pageable); |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.audit; |
|||
|
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import com.google.common.util.concurrent.ListeningExecutorService; |
|||
import com.google.common.util.concurrent.MoreExecutors; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.domain.PageRequest; |
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
import org.thingsboard.server.dao.DaoUtil; |
|||
import org.thingsboard.server.dao.audit.AuditLogDao; |
|||
import org.thingsboard.server.dao.model.sql.AuditLogEntity; |
|||
import org.thingsboard.server.dao.sql.JpaAbstractDao; |
|||
import org.thingsboard.server.dao.util.SqlDao; |
|||
|
|||
import javax.annotation.PreDestroy; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; |
|||
|
|||
@Component |
|||
@SqlDao |
|||
public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> implements AuditLogDao { |
|||
|
|||
private ListeningExecutorService insertService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); |
|||
|
|||
@Autowired |
|||
private AuditLogRepository auditLogRepository; |
|||
|
|||
@Override |
|||
protected Class<AuditLogEntity> getEntityClass() { |
|||
return AuditLogEntity.class; |
|||
} |
|||
|
|||
@Override |
|||
protected CrudRepository<AuditLogEntity, String> getCrudRepository() { |
|||
return auditLogRepository; |
|||
} |
|||
|
|||
@PreDestroy |
|||
void onDestroy() { |
|||
insertService.shutdown(); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> saveByTenantId(AuditLog auditLog) { |
|||
return insertService.submit(() -> { |
|||
save(auditLog); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> saveByTenantIdAndEntityId(AuditLog auditLog) { |
|||
return insertService.submit(() -> null); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> savePartitionsByTenantId(AuditLog auditLog) { |
|||
return insertService.submit(() -> null); |
|||
} |
|||
|
|||
@Override |
|||
public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) { |
|||
return DaoUtil.convertDataList( |
|||
auditLogRepository.findByTenantIdAndEntityId( |
|||
fromTimeUUID(tenantId), |
|||
fromTimeUUID(entityId.getId()), |
|||
entityId.getEntityType(), |
|||
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), |
|||
new PageRequest(0, pageLink.getLimit()))); |
|||
} |
|||
|
|||
@Override |
|||
public List<AuditLog> findAuditLogsByTenantId(UUID tenantId, TimePageLink pageLink) { |
|||
return DaoUtil.convertDataList( |
|||
auditLogRepository.findByTenantId( |
|||
fromTimeUUID(tenantId), |
|||
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), |
|||
new PageRequest(0, pageLink.getLimit()))); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.audit; |
|||
|
|||
import org.thingsboard.server.dao.AbstractJpaDaoTest; |
|||
|
|||
public class JpaAuditLogDaoTest extends AbstractJpaDaoTest { |
|||
} |
|||
Loading…
Reference in new issue