68 changed files with 2211 additions and 41 deletions
@ -0,0 +1,257 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.service.device; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import com.google.common.util.concurrent.MoreExecutors; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.util.StringUtils; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.UserId; |
||||
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.StringDataEntry; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.common.msg.TbMsg; |
||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
||||
|
import org.thingsboard.server.common.msg.queue.ServiceType; |
||||
|
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
||||
|
import org.thingsboard.server.dao.attributes.AttributesService; |
||||
|
import org.thingsboard.server.dao.audit.AuditLogService; |
||||
|
import org.thingsboard.server.dao.device.DeviceCredentialsService; |
||||
|
import org.thingsboard.server.dao.device.DeviceDao; |
||||
|
import org.thingsboard.server.dao.device.DeviceProfileDao; |
||||
|
import org.thingsboard.server.dao.device.DeviceProvisionService; |
||||
|
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.ProvisionResponse; |
||||
|
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; |
||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
||||
|
import org.thingsboard.server.queue.TbQueueCallback; |
||||
|
import org.thingsboard.server.queue.TbQueueProducer; |
||||
|
import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
||||
|
import org.thingsboard.server.queue.discovery.PartitionService; |
||||
|
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.state.DeviceStateService; |
||||
|
|
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
import java.util.concurrent.ExecutionException; |
||||
|
import java.util.concurrent.locks.ReentrantLock; |
||||
|
|
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
||||
|
|
||||
|
protected TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> ruleEngineMsgProducer; |
||||
|
|
||||
|
private static final String DEVICE_PROVISION_STATE = "provisionState"; |
||||
|
private static final String PROVISIONED_STATE = "provisioned"; |
||||
|
|
||||
|
private final ReentrantLock deviceCreationLock = new ReentrantLock(); |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceDao deviceDao; |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceProfileDao deviceProfileDao; |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceService deviceService; |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceCredentialsService deviceCredentialsService; |
||||
|
|
||||
|
@Autowired |
||||
|
AttributesService attributesService; |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceStateService deviceStateService; |
||||
|
|
||||
|
@Autowired |
||||
|
AuditLogService auditLogService; |
||||
|
|
||||
|
@Autowired |
||||
|
PartitionService partitionService; |
||||
|
|
||||
|
public DeviceProvisionServiceImpl(TbQueueProducerProvider producerProvider) { |
||||
|
ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) { |
||||
|
String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey(); |
||||
|
String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); |
||||
|
|
||||
|
if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) { |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
||||
|
} |
||||
|
|
||||
|
DeviceProfile targetProfile = deviceProfileDao.findByProvisionDeviceKey(provisionRequestKey); |
||||
|
|
||||
|
if (targetProfile == null || targetProfile.getProfileData().getProvisionConfiguration() == null || |
||||
|
targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() == null) { |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
||||
|
} |
||||
|
|
||||
|
Device targetDevice = deviceDao.findDeviceByTenantIdAndName(targetProfile.getTenantId().getId(), provisionRequest.getDeviceName()).orElse(null); |
||||
|
|
||||
|
switch (targetProfile.getProvisionType()) { |
||||
|
case ALLOW_CREATE_NEW_DEVICES: |
||||
|
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { |
||||
|
if (targetDevice != null) { |
||||
|
log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName()); |
||||
|
notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
||||
|
} else { |
||||
|
return createDevice(provisionRequest, targetProfile); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
case CHECK_PRE_PROVISIONED_DEVICES: |
||||
|
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { |
||||
|
if (targetDevice != null && targetDevice.getDeviceProfileId().equals(targetProfile.getId())) { |
||||
|
return processProvision(targetDevice, provisionRequest); |
||||
|
} else { |
||||
|
log.warn("[{}] Failed to find pre provisioned device!", provisionRequest.getDeviceName()); |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
||||
|
} |
||||
|
|
||||
|
private ProvisionResponse processProvision(Device device, ProvisionRequest provisionRequest) { |
||||
|
try { |
||||
|
Optional<AttributeKvEntry> provisionState = attributesService.find(device.getTenantId(), device.getId(), |
||||
|
DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE).get(); |
||||
|
if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) { |
||||
|
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
||||
|
} else { |
||||
|
saveProvisionStateAttribute(device).get(); |
||||
|
notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); |
||||
|
} |
||||
|
} catch (InterruptedException | ExecutionException e) { |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
||||
|
} |
||||
|
return new ProvisionResponse(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()), ProvisionResponseStatus.SUCCESS); |
||||
|
} |
||||
|
|
||||
|
private ProvisionResponse createDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
||||
|
deviceCreationLock.lock(); |
||||
|
try { |
||||
|
return processCreateDevice(provisionRequest, profile); |
||||
|
} finally { |
||||
|
deviceCreationLock.unlock(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) { |
||||
|
pushProvisionEventToRuleEngine(provisionRequest, device, type); |
||||
|
logAction(device.getTenantId(), device.getCustomerId(), device, success, provisionRequest); |
||||
|
} |
||||
|
|
||||
|
private ProvisionResponse processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { |
||||
|
Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); |
||||
|
try { |
||||
|
if (device == null) { |
||||
|
Device savedDevice = deviceService.saveDevice(provisionRequest, profile); |
||||
|
|
||||
|
deviceStateService.onDeviceAdded(savedDevice); |
||||
|
saveProvisionStateAttribute(savedDevice).get(); |
||||
|
pushDeviceCreatedEventToRuleEngine(savedDevice); |
||||
|
notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true); |
||||
|
|
||||
|
return new ProvisionResponse(getDeviceCredentials(savedDevice), ProvisionResponseStatus.SUCCESS); |
||||
|
} else { |
||||
|
log.warn("[{}] The device is already provisioned!", device.getName()); |
||||
|
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
||||
|
} |
||||
|
} catch (InterruptedException | ExecutionException e) { |
||||
|
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<List<Void>> saveProvisionStateAttribute(Device device) { |
||||
|
return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, |
||||
|
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE), |
||||
|
System.currentTimeMillis()))); |
||||
|
} |
||||
|
|
||||
|
private DeviceCredentials getDeviceCredentials(Device device) { |
||||
|
return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); |
||||
|
} |
||||
|
|
||||
|
private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { |
||||
|
try { |
||||
|
JsonNode entityNode = JacksonUtil.valueToTree(request); |
||||
|
TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); |
||||
|
sendToRuleEngine(device.getTenantId(), msg, null); |
||||
|
} catch (IllegalArgumentException e) { |
||||
|
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void pushDeviceCreatedEventToRuleEngine(Device device) { |
||||
|
try { |
||||
|
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); |
||||
|
TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); |
||||
|
sendToRuleEngine(device.getTenantId(), msg, null); |
||||
|
} catch (JsonProcessingException | IllegalArgumentException e) { |
||||
|
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { |
||||
|
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator()); |
||||
|
TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder().setTbMsg(TbMsg.toByteString(tbMsg)) |
||||
|
.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
||||
|
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build(); |
||||
|
ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback); |
||||
|
} |
||||
|
|
||||
|
private TbMsgMetaData createTbMsgMetaData(Device device) { |
||||
|
TbMsgMetaData metaData = new TbMsgMetaData(); |
||||
|
metaData.putValue("tenantId", device.getTenantId().toString()); |
||||
|
return metaData; |
||||
|
} |
||||
|
|
||||
|
private void logAction(TenantId tenantId, CustomerId customerId, Device device, boolean success, ProvisionRequest provisionRequest) { |
||||
|
ActionType actionType = success ? ActionType.PROVISION_SUCCESS : ActionType.PROVISION_FAILURE; |
||||
|
auditLogService.logEntityAction(tenantId, customerId, new UserId(UserId.NULL_UUID), device.getName(), device.getId(), device, actionType, null, provisionRequest); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,285 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.provision; |
||||
|
|
||||
|
import com.google.gson.JsonObject; |
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.common.msg.EncryptionUtil; |
||||
|
import org.thingsboard.server.common.transport.util.JsonUtils; |
||||
|
import org.thingsboard.server.dao.device.DeviceCredentialsService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; |
||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
||||
|
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
||||
|
|
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest { |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceCredentialsService deviceCredentialsService; |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceService deviceService; |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningDisabledDevice() throws Exception { |
||||
|
processTestProvisioningDisabledDevice(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCheckPreProvisionedDevice() throws Exception { |
||||
|
processTestProvisioningCheckPreProvisionedDevice(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithoutCredentials() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithoutCredentials(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithAccessToken() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithAccessToken(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithCert() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithCert(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithMqttBasic() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithMqttBasic(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningWithBadKeyDevice() throws Exception { |
||||
|
processTestProvisioningWithBadKeyDevice(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestProvisioningDisabledDevice() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.DISABLED, null, null); |
||||
|
byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\""; |
||||
|
byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "ACCESS_TOKEN"); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\""; |
||||
|
byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "X509_CERTIFICATE"); |
||||
|
|
||||
|
String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); |
||||
|
String sha3Hash = EncryptionUtil.getSha3Hash(cert); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\""; |
||||
|
byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "MQTT_BASIC"); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username")); |
||||
|
|
||||
|
BasicMqttCredentials mqttCredentials = new BasicMqttCredentials(); |
||||
|
mqttCredentials.setClientId("test_clientId"); |
||||
|
mqttCredentials.setUserName("test_username"); |
||||
|
mqttCredentials.setPassword("test_password"); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials)); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
Assert.assertEquals(savedDevice.getId().toString(), response.get("deviceId").getAsString()); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningWithBadKeyDevice() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); |
||||
|
byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
||||
|
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
||||
|
Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("provisionDeviceStatus").getAsString()); |
||||
|
} |
||||
|
|
||||
|
protected TestMqttCallback createMqttClientAndPublish() throws Exception { |
||||
|
return createMqttClientAndPublish(""); |
||||
|
} |
||||
|
|
||||
|
protected TestMqttCallback createMqttClientAndPublish(String deviceCredentials) throws Exception { |
||||
|
String provisionRequestMsg = createTestProvisionMessage(deviceCredentials); |
||||
|
MqttAsyncClient client = getMqttAsyncClient("provision"); |
||||
|
TestMqttCallback onProvisionCallback = getTestMqttCallback(); |
||||
|
client.setCallback(onProvisionCallback); |
||||
|
client.subscribe(MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
Thread.sleep(2000); |
||||
|
client.publish(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC, new MqttMessage(provisionRequestMsg.getBytes())); |
||||
|
onProvisionCallback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
return onProvisionCallback; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected TestMqttCallback getTestMqttCallback() { |
||||
|
CountDownLatch latch = new CountDownLatch(1); |
||||
|
return new TestMqttCallback(latch); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected static class TestMqttCallback implements MqttCallback { |
||||
|
|
||||
|
private final CountDownLatch latch; |
||||
|
private Integer qoS; |
||||
|
private byte[] payloadBytes; |
||||
|
|
||||
|
TestMqttCallback(CountDownLatch latch) { |
||||
|
this.latch = latch; |
||||
|
} |
||||
|
|
||||
|
public int getQoS() { |
||||
|
return qoS; |
||||
|
} |
||||
|
|
||||
|
public byte[] getPayloadBytes() { |
||||
|
return payloadBytes; |
||||
|
} |
||||
|
|
||||
|
public CountDownLatch getLatch() { |
||||
|
return latch; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void connectionLost(Throwable throwable) { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
||||
|
qoS = mqttMessage.getQos(); |
||||
|
payloadBytes = mqttMessage.getPayload(); |
||||
|
latch.countDown(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected String createTestProvisionMessage(String deviceCredentials) { |
||||
|
return "{\"deviceName\":\"Test Provision device\",\"provisionDeviceKey\":\"testProvisionKey\", \"provisionDeviceSecret\":\"testProvisionSecret\"" + deviceCredentials + "}"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,305 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.provision; |
||||
|
|
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
||||
|
import org.thingsboard.server.common.msg.EncryptionUtil; |
||||
|
import org.thingsboard.server.dao.device.DeviceCredentialsService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; |
||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.CredentialsDataProto; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.CredentialsType; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceCredentialsMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
||||
|
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest { |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceCredentialsService deviceCredentialsService; |
||||
|
|
||||
|
@Autowired |
||||
|
DeviceService deviceService; |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningDisabledDevice() throws Exception { |
||||
|
processTestProvisioningDisabledDevice(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCheckPreProvisionedDevice() throws Exception { |
||||
|
processTestProvisioningCheckPreProvisionedDevice(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithoutCredentials() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithoutCredentials(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithAccessToken() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithAccessToken(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithCert() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithCert(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningCreateNewDeviceWithMqttBasic() throws Exception { |
||||
|
processTestProvisioningCreateNewDeviceWithMqttBasic(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testProvisioningWithBadKeyDevice() throws Exception { |
||||
|
processTestProvisioningWithBadKeyDevice(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestProvisioningDisabledDevice() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.DISABLED, null, null); |
||||
|
ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
||||
|
Assert.assertNotNull(result); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build(); |
||||
|
|
||||
|
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes()); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.ACCESS_TOKEN); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build(); |
||||
|
|
||||
|
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes()); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.X509_CERTIFICATE); |
||||
|
|
||||
|
String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); |
||||
|
String sha3Hash = EncryptionUtil.getSha3Hash(cert); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateBasicMqttCredRequestMsg( |
||||
|
ValidateBasicMqttCredRequestMsg.newBuilder() |
||||
|
.setClientId("test_clientId") |
||||
|
.setUserName("test_username") |
||||
|
.setPassword("test_password") |
||||
|
.build() |
||||
|
).build(); |
||||
|
|
||||
|
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.MQTT_BASIC, requestCredentials)).getPayloadBytes()); |
||||
|
|
||||
|
Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
||||
|
|
||||
|
Assert.assertNotNull(createdDevice); |
||||
|
Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.MQTT_BASIC); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username")); |
||||
|
|
||||
|
BasicMqttCredentials mqttCredentials = new BasicMqttCredentials(); |
||||
|
mqttCredentials.setClientId("test_clientId"); |
||||
|
mqttCredentials.setUserName("test_username"); |
||||
|
mqttCredentials.setPassword("test_password"); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials)); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); |
||||
|
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
||||
|
Assert.assertEquals(savedDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); |
||||
|
|
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected void processTestProvisioningWithBadKeyDevice() throws Exception { |
||||
|
super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); |
||||
|
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
||||
|
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getProvisionResponseStatus().toString()); |
||||
|
} |
||||
|
|
||||
|
protected TestMqttCallback createMqttClientAndPublish() throws Exception { |
||||
|
byte[] provisionRequestMsg = createTestProvisionMessage(); |
||||
|
return createMqttClientAndPublish(provisionRequestMsg); |
||||
|
} |
||||
|
|
||||
|
protected TestMqttCallback createMqttClientAndPublish(byte[] provisionRequestMsg) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient("provision"); |
||||
|
TestMqttCallback onProvisionCallback = getTestMqttCallback(); |
||||
|
client.setCallback(onProvisionCallback); |
||||
|
client.subscribe(MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
Thread.sleep(2000); |
||||
|
client.publish(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC, new MqttMessage(provisionRequestMsg)); |
||||
|
onProvisionCallback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
return onProvisionCallback; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected TestMqttCallback getTestMqttCallback() { |
||||
|
CountDownLatch latch = new CountDownLatch(1); |
||||
|
return new TestMqttCallback(latch); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected static class TestMqttCallback implements MqttCallback { |
||||
|
|
||||
|
private final CountDownLatch latch; |
||||
|
private Integer qoS; |
||||
|
private byte[] payloadBytes; |
||||
|
|
||||
|
TestMqttCallback(CountDownLatch latch) { |
||||
|
this.latch = latch; |
||||
|
} |
||||
|
|
||||
|
public int getQoS() { |
||||
|
return qoS; |
||||
|
} |
||||
|
|
||||
|
public byte[] getPayloadBytes() { |
||||
|
return payloadBytes; |
||||
|
} |
||||
|
|
||||
|
public CountDownLatch getLatch() { |
||||
|
return latch; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void connectionLost(Throwable throwable) { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
||||
|
qoS = mqttMessage.getQos(); |
||||
|
payloadBytes = mqttMessage.getPayload(); |
||||
|
latch.countDown(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected byte[] createTestsProvisionMessage(CredentialsType credentialsType, CredentialsDataProto credentialsData) throws Exception { |
||||
|
return ProvisionDeviceRequestMsg.newBuilder() |
||||
|
.setDeviceName("Test Provision device") |
||||
|
.setCredentialsType(credentialsType != null ? credentialsType : CredentialsType.ACCESS_TOKEN) |
||||
|
.setCredentialsDataProto(credentialsData != null ? credentialsData: CredentialsDataProto.newBuilder().build()) |
||||
|
.setProvisionDeviceCredentialsMsg( |
||||
|
ProvisionDeviceCredentialsMsg.newBuilder() |
||||
|
.setProvisionDeviceKey("testProvisionKey") |
||||
|
.setProvisionDeviceSecret("testProvisionSecret") |
||||
|
).build() |
||||
|
.toByteArray(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected byte[] createTestProvisionMessage() throws Exception { |
||||
|
return createTestsProvisionMessage(null, null); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.provision.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.provision.AbstractMqttProvisionJsonDeviceTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttProvisionDeviceJsonSqlTest extends AbstractMqttProvisionJsonDeviceTest { |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.provision.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.provision.AbstractMqttProvisionProtoDeviceTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttProvisionDeviceProtoSqlTest extends AbstractMqttProvisionProtoDeviceTest { |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device; |
||||
|
|
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.dao.device.provision.ProvisionFailedException; |
||||
|
import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
||||
|
import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
||||
|
|
||||
|
public interface DeviceProvisionService { |
||||
|
|
||||
|
ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) throws ProvisionFailedException; |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.provision; |
||||
|
|
||||
|
public class ProvisionFailedException extends RuntimeException { |
||||
|
public ProvisionFailedException(String errorMsg) { |
||||
|
super(errorMsg); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.provision; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; |
||||
|
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
||||
|
|
||||
|
@Data |
||||
|
@AllArgsConstructor |
||||
|
public class ProvisionRequest { |
||||
|
private String deviceName; |
||||
|
private DeviceCredentialsType credentialsType; |
||||
|
private ProvisionDeviceCredentialsData credentialsData; |
||||
|
private ProvisionDeviceProfileCredentials credentials; |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.provision; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
|
||||
|
@Data |
||||
|
public class ProvisionResponse { |
||||
|
private final DeviceCredentials deviceCredentials; |
||||
|
private final ProvisionResponseStatus responseStatus; |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.provision; |
||||
|
|
||||
|
public enum ProvisionResponseStatus { |
||||
|
UNKNOWN, |
||||
|
SUCCESS, |
||||
|
NOT_FOUND, |
||||
|
FAILURE |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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; |
||||
|
|
||||
|
public enum DeviceProfileProvisionType { |
||||
|
DISABLED, |
||||
|
ALLOW_CREATE_NEW_DEVICES, |
||||
|
CHECK_PRE_PROVISIONED_DEVICES |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.credentials; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class ProvisionDeviceCredentialsData { |
||||
|
private final String token; |
||||
|
private final String clientId; |
||||
|
private final String username; |
||||
|
private final String password; |
||||
|
private final String x509CertHash; |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
|
||||
|
@Data |
||||
|
public class AllowCreateNewDevicesDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { |
||||
|
|
||||
|
private final String provisionDeviceSecret; |
||||
|
|
||||
|
@Override |
||||
|
public DeviceProfileProvisionType getType() { |
||||
|
return DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
|
||||
|
@Data |
||||
|
public class CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { |
||||
|
|
||||
|
private final String provisionDeviceSecret; |
||||
|
|
||||
|
@Override |
||||
|
public DeviceProfileProvisionType getType() { |
||||
|
return DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.profile; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes; |
||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
|
||||
|
|
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
@JsonTypeInfo( |
||||
|
use = JsonTypeInfo.Id.NAME, |
||||
|
include = JsonTypeInfo.As.PROPERTY, |
||||
|
property = "type") |
||||
|
@JsonSubTypes({ |
||||
|
@JsonSubTypes.Type(value = DisabledDeviceProfileProvisionConfiguration.class, name = "DISABLED"), |
||||
|
@JsonSubTypes.Type(value = AllowCreateNewDevicesDeviceProfileProvisionConfiguration.class, name = "ALLOW_CREATE_NEW_DEVICES"), |
||||
|
@JsonSubTypes.Type(value = CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.class, name = "CHECK_PRE_PROVISIONED_DEVICES")}) |
||||
|
public interface DeviceProfileProvisionConfiguration { |
||||
|
|
||||
|
String getProvisionDeviceSecret(); |
||||
|
|
||||
|
@JsonIgnore |
||||
|
DeviceProfileProvisionType getType(); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
|
||||
|
@Data |
||||
|
public class DisabledDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { |
||||
|
|
||||
|
private final String provisionDeviceSecret; |
||||
|
|
||||
|
@Override |
||||
|
public DeviceProfileProvisionType getType() { |
||||
|
return DeviceProfileProvisionType.DISABLED; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class ProvisionDeviceProfileCredentials { |
||||
|
private final String provisionDeviceKey; |
||||
|
private final String provisionDeviceSecret; |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2020 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. |
||||
|
|
||||
|
--> |
||||
|
<div [formGroup]="provisionConfigurationFormGroup"> |
||||
|
<mat-form-field class="mat-block"> |
||||
|
<mat-label translate>device-profile.provision-strategy</mat-label> |
||||
|
<mat-select formControlName="type" required> |
||||
|
<mat-option *ngFor="let type of deviceProvisionTypes" [value]="type"> |
||||
|
{{deviceProvisionTypeTranslateMap.get(type) | translate}} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
<mat-error *ngIf="provisionConfigurationFormGroup.get('type').hasError('required')"> |
||||
|
{{ 'device-profile.provision-strategy-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<section *ngIf="provisionConfigurationFormGroup.get('type').value !== deviceProvisionType.DISABLED" fxLayoutGap.gt-xs="8px" fxLayout="row" fxLayout.xs="column"> |
||||
|
<mat-form-field fxFlex class="mat-block"> |
||||
|
<mat-label translate>device-profile.provision-device-key</mat-label> |
||||
|
<input matInput formControlName="provisionDeviceKey" required/> |
||||
|
<mat-error *ngIf="provisionConfigurationFormGroup.get('provisionDeviceKey').hasError('required')"> |
||||
|
{{ 'device-profile.provision-device-key-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field fxFlex class="mat-block"> |
||||
|
<mat-label translate>device-profile.provision-device-secret</mat-label> |
||||
|
<input matInput formControlName="provisionDeviceSecret" required/> |
||||
|
<mat-error *ngIf="provisionConfigurationFormGroup.get('provisionDeviceSecret').hasError('required')"> |
||||
|
{{ 'device-profile.provision-device-secret-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
|
</div> |
||||
@ -0,0 +1,140 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2020 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, forwardRef, Input, OnInit } from "@angular/core"; |
||||
|
import { |
||||
|
ControlValueAccessor, |
||||
|
FormBuilder, |
||||
|
FormControl, |
||||
|
FormGroup, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
ValidationErrors, |
||||
|
Validator, |
||||
|
Validators |
||||
|
} from "@angular/forms"; |
||||
|
import { coerceBooleanProperty } from "@angular/cdk/coercion"; |
||||
|
import { |
||||
|
DeviceProvisionConfiguration, |
||||
|
DeviceProvisionType, |
||||
|
deviceProvisionTypeTranslationMap |
||||
|
} from "@shared/models/device.models"; |
||||
|
import { isDefinedAndNotNull } from "@core/utils"; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-device-profile-provision-configuration', |
||||
|
templateUrl: './device-profile-provision-configuration.component.html', |
||||
|
styleUrls: [], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => DeviceProfileProvisionConfigurationComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => DeviceProfileProvisionConfigurationComponent), |
||||
|
multi: true, |
||||
|
} |
||||
|
] |
||||
|
}) |
||||
|
export class DeviceProfileProvisionConfigurationComponent implements ControlValueAccessor, OnInit, Validator { |
||||
|
|
||||
|
provisionConfigurationFormGroup: FormGroup; |
||||
|
|
||||
|
deviceProvisionType = DeviceProvisionType; |
||||
|
deviceProvisionTypes = Object.keys(DeviceProvisionType); |
||||
|
deviceProvisionTypeTranslateMap = deviceProvisionTypeTranslationMap; |
||||
|
|
||||
|
private requiredValue: boolean; |
||||
|
get required(): boolean { |
||||
|
return this.requiredValue; |
||||
|
} |
||||
|
@Input() |
||||
|
set required(value: boolean) { |
||||
|
this.requiredValue = coerceBooleanProperty(value); |
||||
|
} |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
private propagateChange = (v: any) => { }; |
||||
|
|
||||
|
constructor(private fb: FormBuilder) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.provisionConfigurationFormGroup = this.fb.group({ |
||||
|
type: [DeviceProvisionType.DISABLED, Validators.required], |
||||
|
provisionDeviceSecret: [{value: null, disabled: true}, Validators.required], |
||||
|
provisionDeviceKey: [{value: null, disabled: true}, Validators.required] |
||||
|
}); |
||||
|
this.provisionConfigurationFormGroup.get('type').valueChanges.subscribe((type) => { |
||||
|
if (type === DeviceProvisionType.DISABLED) { |
||||
|
this.provisionConfigurationFormGroup.get('provisionDeviceSecret').disable({emitEvent: false}); |
||||
|
this.provisionConfigurationFormGroup.get('provisionDeviceSecret').patchValue(null,{emitEvent: false}); |
||||
|
this.provisionConfigurationFormGroup.get('provisionDeviceKey').disable({emitEvent: false}); |
||||
|
this.provisionConfigurationFormGroup.get('provisionDeviceKey').patchValue(null); |
||||
|
} else { |
||||
|
this.provisionConfigurationFormGroup.get('provisionDeviceSecret').enable({emitEvent: false}); |
||||
|
this.provisionConfigurationFormGroup.get('provisionDeviceKey').enable({emitEvent: false}); |
||||
|
} |
||||
|
}); |
||||
|
this.provisionConfigurationFormGroup.valueChanges.subscribe(() => { |
||||
|
this.updateModel(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
writeValue(value: DeviceProvisionConfiguration | null): void { |
||||
|
if (isDefinedAndNotNull(value)){ |
||||
|
this.provisionConfigurationFormGroup.patchValue(value, {emitEvent: false}); |
||||
|
} else { |
||||
|
this.provisionConfigurationFormGroup.patchValue({type: DeviceProvisionType.DISABLED}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean){ |
||||
|
this.disabled = isDisabled; |
||||
|
if (this.disabled){ |
||||
|
this.provisionConfigurationFormGroup.disable(); |
||||
|
} else { |
||||
|
this.provisionConfigurationFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
validate(c: FormControl): ValidationErrors | null { |
||||
|
return (this.provisionConfigurationFormGroup.valid) ? null : { |
||||
|
provisionConfiguration: { |
||||
|
valid: false, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private updateModel(): void { |
||||
|
let deviceProvisionConfiguration: DeviceProvisionConfiguration = null; |
||||
|
if (this.provisionConfigurationFormGroup.valid) { |
||||
|
deviceProvisionConfiguration = this.provisionConfigurationFormGroup.getRawValue(); |
||||
|
} |
||||
|
this.propagateChange(deviceProvisionConfiguration); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue