|
|
@ -16,6 +16,7 @@ |
|
|
package org.thingsboard.server.service.device; |
|
|
package org.thingsboard.server.service.device; |
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException; |
|
|
import com.fasterxml.jackson.core.JsonProcessingException; |
|
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode; |
|
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
|
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
|
|
import com.google.common.util.concurrent.Futures; |
|
|
import com.google.common.util.concurrent.Futures; |
|
|
import com.google.common.util.concurrent.ListenableFuture; |
|
|
import com.google.common.util.concurrent.ListenableFuture; |
|
|
@ -28,9 +29,6 @@ import org.thingsboard.server.common.data.DataConstants; |
|
|
import org.thingsboard.server.common.data.Device; |
|
|
import org.thingsboard.server.common.data.Device; |
|
|
import org.thingsboard.server.common.data.DeviceProfile; |
|
|
import org.thingsboard.server.common.data.DeviceProfile; |
|
|
import org.thingsboard.server.common.data.audit.ActionType; |
|
|
import org.thingsboard.server.common.data.audit.ActionType; |
|
|
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
|
|
|
|
|
import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; |
|
|
|
|
|
import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration; |
|
|
|
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
import org.thingsboard.server.common.data.id.UserId; |
|
|
import org.thingsboard.server.common.data.id.UserId; |
|
|
@ -49,6 +47,7 @@ import org.thingsboard.server.dao.device.DeviceDao; |
|
|
import org.thingsboard.server.dao.device.DeviceProfileDao; |
|
|
import org.thingsboard.server.dao.device.DeviceProfileDao; |
|
|
import org.thingsboard.server.dao.device.DeviceProvisionService; |
|
|
import org.thingsboard.server.dao.device.DeviceProvisionService; |
|
|
import org.thingsboard.server.dao.device.DeviceService; |
|
|
import org.thingsboard.server.dao.device.DeviceService; |
|
|
|
|
|
import org.thingsboard.server.dao.device.provision.ProvisionFailedException; |
|
|
import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
|
|
import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
|
|
import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
|
|
import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
|
|
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; |
|
|
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; |
|
|
@ -115,81 +114,55 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
|
|
String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); |
|
|
String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); |
|
|
|
|
|
|
|
|
if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) { |
|
|
if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) { |
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND)); |
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (provisionRequest.getCredentialsType() != null) { |
|
|
|
|
|
ListenableFuture<ProvisionResponse> error = validateCredentials(provisionRequest); |
|
|
|
|
|
if (error != null) { |
|
|
|
|
|
return error; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
DeviceProfile targetProfile = deviceProfileDao.findByProvisionDeviceKey(provisionRequestKey); |
|
|
DeviceProfile targetProfile = deviceProfileDao.findByProvisionDeviceKey(provisionRequestKey); |
|
|
|
|
|
|
|
|
if (targetProfile == null) { |
|
|
if (targetProfile == null || targetProfile.getProfileData().getProvisionConfiguration() == null || |
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND)); |
|
|
targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() == null) { |
|
|
|
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Device targetDevice = deviceDao.findDeviceByTenantIdAndName(targetProfile.getTenantId().getId(), provisionRequest.getDeviceName()).orElse(null); |
|
|
Device targetDevice = deviceDao.findDeviceByTenantIdAndName(targetProfile.getTenantId().getId(), provisionRequest.getDeviceName()).orElse(null); |
|
|
|
|
|
|
|
|
switch (targetProfile.getProvisionType()) { |
|
|
switch (targetProfile.getProvisionType()) { |
|
|
case ALLOW_CREATE_NEW_DEVICES: |
|
|
case ALLOW_CREATE_NEW_DEVICES: |
|
|
if (((AllowCreateNewDevicesDeviceProfileProvisionConfiguration) targetProfile.getProfileData().getProvisionConfiguration()).getProvisionDeviceSecret().equals(provisionRequestSecret)) { |
|
|
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { |
|
|
if (targetDevice != null) { |
|
|
if (targetDevice != null) { |
|
|
log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName()); |
|
|
log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName()); |
|
|
notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|
|
notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
|
|
} else { |
|
|
} else { |
|
|
return createDevice(provisionRequest, targetProfile); |
|
|
return createDevice(provisionRequest, targetProfile); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
case CHECK_PRE_PROVISIONED_DEVICES: |
|
|
case CHECK_PRE_PROVISIONED_DEVICES: |
|
|
if (((CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration) targetProfile.getProfileData().getProvisionConfiguration()).getProvisionDeviceSecret().equals(provisionRequestSecret)) { |
|
|
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { |
|
|
if (targetDevice != null && targetDevice.getDeviceProfileId().equals(targetProfile.getId())) { |
|
|
if (targetDevice != null && targetDevice.getDeviceProfileId().equals(targetProfile.getId())) { |
|
|
return processProvision(targetDevice, provisionRequest); |
|
|
return processProvision(targetDevice, provisionRequest); |
|
|
} else { |
|
|
} else { |
|
|
log.warn("[{}] Failed to find pre provisioned device!", provisionRequest.getDeviceName()); |
|
|
log.warn("[{}] Failed to find pre provisioned device!", provisionRequest.getDeviceName()); |
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND)); |
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private ListenableFuture<ProvisionResponse> validateCredentials(ProvisionRequest provisionRequest) { |
|
|
|
|
|
switch (provisionRequest.getCredentialsType()) { |
|
|
|
|
|
case MQTT_BASIC: |
|
|
|
|
|
if (StringUtils.isEmpty(provisionRequest.getCredentialsData().getClientId()) || |
|
|
|
|
|
StringUtils.isEmpty(provisionRequest.getCredentialsData().getUsername()) || |
|
|
|
|
|
StringUtils.isEmpty(provisionRequest.getCredentialsData().getPassword())) { |
|
|
|
|
|
log.error("Failed to get basic mqtt credentials from credentials data!"); |
|
|
|
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case X509_CERTIFICATE: |
|
|
|
|
|
if (StringUtils.isEmpty(provisionRequest.getCredentialsData().getHash())) { |
|
|
|
|
|
log.error("Failed to get hash from credentials data!"); |
|
|
|
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private ListenableFuture<ProvisionResponse> processProvision(Device device, ProvisionRequest provisionRequest) { |
|
|
private ListenableFuture<ProvisionResponse> processProvision(Device device, ProvisionRequest provisionRequest) { |
|
|
ListenableFuture<Optional<AttributeKvEntry>> provisionStateFuture = attributesService.find(device.getTenantId(), device.getId(), |
|
|
ListenableFuture<Optional<AttributeKvEntry>> provisionStateFuture = attributesService.find(device.getTenantId(), device.getId(), |
|
|
DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE); |
|
|
DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE); |
|
|
ListenableFuture<Boolean> provisionedFuture = Futures.transformAsync(provisionStateFuture, optionalAtr -> { |
|
|
ListenableFuture<Boolean> provisionedFuture = Futures.transformAsync(provisionStateFuture, optionalAtr -> { |
|
|
if (optionalAtr.isPresent()) { |
|
|
if (optionalAtr != null && optionalAtr.isPresent()) { |
|
|
String state = optionalAtr.get().getValueAsString(); |
|
|
String state = optionalAtr.get().getValueAsString(); |
|
|
if (state.equals(PROVISIONED_STATE)) { |
|
|
if (state.equals(PROVISIONED_STATE)) { |
|
|
return Futures.immediateFuture(true); |
|
|
return Futures.immediateFuture(true); |
|
|
} else { |
|
|
} else { |
|
|
log.error("[{}][{}] Unknown provision state: {}!", device.getName(), DEVICE_PROVISION_STATE, state); |
|
|
log.error("[{}][{}] Unknown provision state: {}!", device.getName(), DEVICE_PROVISION_STATE, state); |
|
|
return Futures.immediateCancelledFuture(); |
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return Futures.transform(saveProvisionStateAttribute(device), input -> false, MoreExecutors.directExecutor()); |
|
|
return Futures.transform(saveProvisionStateAttribute(device), input -> false, MoreExecutors.directExecutor()); |
|
|
@ -200,7 +173,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
|
|
return Futures.transform(provisionedFuture, provisioned -> { |
|
|
return Futures.transform(provisionedFuture, provisioned -> { |
|
|
if (provisioned) { |
|
|
if (provisioned) { |
|
|
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|
|
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|
|
return new ProvisionResponse(null, ProvisionResponseStatus.FAILURE); |
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
|
|
} |
|
|
} |
|
|
notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); |
|
|
notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); |
|
|
return new ProvisionResponse(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()), ProvisionResponseStatus.SUCCESS); |
|
|
return new ProvisionResponse(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()), ProvisionResponseStatus.SUCCESS); |
|
|
@ -224,7 +197,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
|
|
private ListenableFuture<ProvisionResponse> processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
|
|
private ListenableFuture<ProvisionResponse> processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
|
|
Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); |
|
|
Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); |
|
|
if (device == null) { |
|
|
if (device == null) { |
|
|
Device savedDevice = saveDevice(provisionRequest, profile); |
|
|
Device savedDevice = deviceService.saveDevice(provisionRequest, profile); |
|
|
|
|
|
|
|
|
deviceStateService.onDeviceAdded(savedDevice); |
|
|
deviceStateService.onDeviceAdded(savedDevice); |
|
|
pushDeviceCreatedEventToRuleEngine(savedDevice); |
|
|
pushDeviceCreatedEventToRuleEngine(savedDevice); |
|
|
@ -234,10 +207,11 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
|
|
new ProvisionResponse( |
|
|
new ProvisionResponse( |
|
|
getDeviceCredentials(savedDevice), |
|
|
getDeviceCredentials(savedDevice), |
|
|
ProvisionResponseStatus.SUCCESS), MoreExecutors.directExecutor()); |
|
|
ProvisionResponseStatus.SUCCESS), MoreExecutors.directExecutor()); |
|
|
|
|
|
} else { |
|
|
|
|
|
log.warn("[{}] The device is already provisioned!", device.getName()); |
|
|
|
|
|
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|
|
|
|
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
|
|
} |
|
|
} |
|
|
log.warn("[{}] The device is already provisioned!", device.getName()); |
|
|
|
|
|
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
|
|
|
|
|
return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE)); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private ListenableFuture<List<Void>> saveProvisionStateAttribute(Device device) { |
|
|
private ListenableFuture<List<Void>> saveProvisionStateAttribute(Device device) { |
|
|
@ -246,51 +220,16 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
|
|
System.currentTimeMillis()))); |
|
|
System.currentTimeMillis()))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
|
|
|
|
|
Device device = new Device(); |
|
|
|
|
|
device.setName(provisionRequest.getDeviceName()); |
|
|
|
|
|
device.setType(profile.getName()); |
|
|
|
|
|
device.setTenantId(profile.getTenantId()); |
|
|
|
|
|
Device savedDevice = deviceService.saveDevice(device); |
|
|
|
|
|
if (!StringUtils.isEmpty(provisionRequest.getCredentialsData().getToken()) || |
|
|
|
|
|
!StringUtils.isEmpty(provisionRequest.getCredentialsData().getHash()) || |
|
|
|
|
|
!StringUtils.isEmpty(provisionRequest.getCredentialsData().getUsername()) || |
|
|
|
|
|
!StringUtils.isEmpty(provisionRequest.getCredentialsData().getPassword()) || |
|
|
|
|
|
!StringUtils.isEmpty(provisionRequest.getCredentialsData().getClientId())) { |
|
|
|
|
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getTenantId(), savedDevice.getId()); |
|
|
|
|
|
deviceCredentials.setCredentialsType(provisionRequest.getCredentialsType()); |
|
|
|
|
|
switch (provisionRequest.getCredentialsType()) { |
|
|
|
|
|
case ACCESS_TOKEN: |
|
|
|
|
|
deviceCredentials.setDeviceId(savedDevice.getId()); |
|
|
|
|
|
deviceCredentials.setCredentialsId(provisionRequest.getCredentialsData().getToken()); |
|
|
|
|
|
break; |
|
|
|
|
|
case MQTT_BASIC: |
|
|
|
|
|
BasicMqttCredentials mqttCredentials = new BasicMqttCredentials(); |
|
|
|
|
|
mqttCredentials.setClientId(provisionRequest.getCredentialsData().getClientId()); |
|
|
|
|
|
mqttCredentials.setUserName(provisionRequest.getCredentialsData().getUsername()); |
|
|
|
|
|
mqttCredentials.setPassword(provisionRequest.getCredentialsData().getPassword()); |
|
|
|
|
|
deviceCredentials.setCredentialsValue(JacksonUtil.toString(mqttCredentials)); |
|
|
|
|
|
break; |
|
|
|
|
|
case X509_CERTIFICATE: |
|
|
|
|
|
deviceCredentials.setCredentialsValue(provisionRequest.getCredentialsData().getHash()); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
deviceCredentials.setCredentialsType(provisionRequest.getCredentialsType()); |
|
|
|
|
|
deviceCredentialsService.updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); |
|
|
|
|
|
} |
|
|
|
|
|
return savedDevice; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private DeviceCredentials getDeviceCredentials(Device device) { |
|
|
private DeviceCredentials getDeviceCredentials(Device device) { |
|
|
return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); |
|
|
return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { |
|
|
private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { |
|
|
try { |
|
|
try { |
|
|
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(request); |
|
|
JsonNode entityNode = JacksonUtil.valueToTree(request); |
|
|
TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); |
|
|
TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); |
|
|
sendToRuleEngine(device.getTenantId(), msg, null); |
|
|
sendToRuleEngine(device.getTenantId(), msg, null); |
|
|
} catch (JsonProcessingException | IllegalArgumentException e) { |
|
|
} catch (IllegalArgumentException e) { |
|
|
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); |
|
|
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|