Showing
68 changed files
with
2211 additions
and
41 deletions
@@ -89,13 +89,16 @@ CREATE TABLE IF NOT EXISTS device_profile ( | @@ -89,13 +89,16 @@ CREATE TABLE IF NOT EXISTS device_profile ( | ||
89 | name varchar(255), | 89 | name varchar(255), |
90 | type varchar(255), | 90 | type varchar(255), |
91 | transport_type varchar(255), | 91 | transport_type varchar(255), |
92 | + provision_type varchar(255), | ||
92 | profile_data jsonb, | 93 | profile_data jsonb, |
93 | description varchar, | 94 | description varchar, |
94 | search_text varchar(255), | 95 | search_text varchar(255), |
95 | is_default boolean, | 96 | is_default boolean, |
96 | tenant_id uuid, | 97 | tenant_id uuid, |
97 | default_rule_chain_id uuid, | 98 | default_rule_chain_id uuid, |
99 | + provision_device_key varchar, | ||
98 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), | 100 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
101 | + CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), | ||
99 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) | 102 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) |
100 | ); | 103 | ); |
101 | 104 |
@@ -700,6 +700,12 @@ public abstract class BaseController { | @@ -700,6 +700,12 @@ public abstract class BaseController { | ||
700 | case ASSIGNED_TO_TENANT: | 700 | case ASSIGNED_TO_TENANT: |
701 | msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; | 701 | msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; |
702 | break; | 702 | break; |
703 | + case PROVISION_SUCCESS: | ||
704 | + msgType = DataConstants.PROVISION_SUCCESS; | ||
705 | + break; | ||
706 | + case PROVISION_FAILURE: | ||
707 | + msgType = DataConstants.PROVISION_FAILURE; | ||
708 | + break; | ||
703 | } | 709 | } |
704 | if (!StringUtils.isEmpty(msgType)) { | 710 | if (!StringUtils.isEmpty(msgType)) { |
705 | try { | 711 | try { |
application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.device; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import com.google.common.util.concurrent.Futures; | ||
22 | +import com.google.common.util.concurrent.ListenableFuture; | ||
23 | +import com.google.common.util.concurrent.MoreExecutors; | ||
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.springframework.beans.factory.annotation.Autowired; | ||
26 | +import org.springframework.stereotype.Service; | ||
27 | +import org.springframework.util.StringUtils; | ||
28 | +import org.thingsboard.server.common.data.DataConstants; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
31 | +import org.thingsboard.server.common.data.audit.ActionType; | ||
32 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
33 | +import org.thingsboard.server.common.data.id.TenantId; | ||
34 | +import org.thingsboard.server.common.data.id.UserId; | ||
35 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
36 | +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | ||
37 | +import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
38 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
39 | +import org.thingsboard.server.common.msg.TbMsg; | ||
40 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | ||
41 | +import org.thingsboard.server.common.msg.queue.ServiceType; | ||
42 | +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | ||
43 | +import org.thingsboard.server.dao.attributes.AttributesService; | ||
44 | +import org.thingsboard.server.dao.audit.AuditLogService; | ||
45 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | ||
46 | +import org.thingsboard.server.dao.device.DeviceDao; | ||
47 | +import org.thingsboard.server.dao.device.DeviceProfileDao; | ||
48 | +import org.thingsboard.server.dao.device.DeviceProvisionService; | ||
49 | +import org.thingsboard.server.dao.device.DeviceService; | ||
50 | +import org.thingsboard.server.dao.device.provision.ProvisionFailedException; | ||
51 | +import org.thingsboard.server.dao.device.provision.ProvisionRequest; | ||
52 | +import org.thingsboard.server.dao.device.provision.ProvisionResponse; | ||
53 | +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; | ||
54 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
55 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
56 | +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; | ||
57 | +import org.thingsboard.server.queue.TbQueueCallback; | ||
58 | +import org.thingsboard.server.queue.TbQueueProducer; | ||
59 | +import org.thingsboard.server.queue.common.TbProtoQueueMsg; | ||
60 | +import org.thingsboard.server.queue.discovery.PartitionService; | ||
61 | +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; | ||
62 | +import org.thingsboard.server.queue.util.TbCoreComponent; | ||
63 | +import org.thingsboard.server.service.state.DeviceStateService; | ||
64 | + | ||
65 | +import java.util.Collections; | ||
66 | +import java.util.List; | ||
67 | +import java.util.Optional; | ||
68 | +import java.util.concurrent.ExecutionException; | ||
69 | +import java.util.concurrent.locks.ReentrantLock; | ||
70 | + | ||
71 | + | ||
72 | +@Service | ||
73 | +@Slf4j | ||
74 | +@TbCoreComponent | ||
75 | +public class DeviceProvisionServiceImpl implements DeviceProvisionService { | ||
76 | + | ||
77 | + protected TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> ruleEngineMsgProducer; | ||
78 | + | ||
79 | + private static final String DEVICE_PROVISION_STATE = "provisionState"; | ||
80 | + private static final String PROVISIONED_STATE = "provisioned"; | ||
81 | + | ||
82 | + private final ReentrantLock deviceCreationLock = new ReentrantLock(); | ||
83 | + | ||
84 | + @Autowired | ||
85 | + DeviceDao deviceDao; | ||
86 | + | ||
87 | + @Autowired | ||
88 | + DeviceProfileDao deviceProfileDao; | ||
89 | + | ||
90 | + @Autowired | ||
91 | + DeviceService deviceService; | ||
92 | + | ||
93 | + @Autowired | ||
94 | + DeviceCredentialsService deviceCredentialsService; | ||
95 | + | ||
96 | + @Autowired | ||
97 | + AttributesService attributesService; | ||
98 | + | ||
99 | + @Autowired | ||
100 | + DeviceStateService deviceStateService; | ||
101 | + | ||
102 | + @Autowired | ||
103 | + AuditLogService auditLogService; | ||
104 | + | ||
105 | + @Autowired | ||
106 | + PartitionService partitionService; | ||
107 | + | ||
108 | + public DeviceProvisionServiceImpl(TbQueueProducerProvider producerProvider) { | ||
109 | + ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer(); | ||
110 | + } | ||
111 | + | ||
112 | + @Override | ||
113 | + public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) { | ||
114 | + String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey(); | ||
115 | + String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); | ||
116 | + | ||
117 | + if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) { | ||
118 | + throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); | ||
119 | + } | ||
120 | + | ||
121 | + DeviceProfile targetProfile = deviceProfileDao.findByProvisionDeviceKey(provisionRequestKey); | ||
122 | + | ||
123 | + if (targetProfile == null || targetProfile.getProfileData().getProvisionConfiguration() == null || | ||
124 | + targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() == null) { | ||
125 | + throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); | ||
126 | + } | ||
127 | + | ||
128 | + Device targetDevice = deviceDao.findDeviceByTenantIdAndName(targetProfile.getTenantId().getId(), provisionRequest.getDeviceName()).orElse(null); | ||
129 | + | ||
130 | + switch (targetProfile.getProvisionType()) { | ||
131 | + case ALLOW_CREATE_NEW_DEVICES: | ||
132 | + if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { | ||
133 | + if (targetDevice != null) { | ||
134 | + log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName()); | ||
135 | + notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false); | ||
136 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
137 | + } else { | ||
138 | + return createDevice(provisionRequest, targetProfile); | ||
139 | + } | ||
140 | + } | ||
141 | + break; | ||
142 | + case CHECK_PRE_PROVISIONED_DEVICES: | ||
143 | + if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { | ||
144 | + if (targetDevice != null && targetDevice.getDeviceProfileId().equals(targetProfile.getId())) { | ||
145 | + return processProvision(targetDevice, provisionRequest); | ||
146 | + } else { | ||
147 | + log.warn("[{}] Failed to find pre provisioned device!", provisionRequest.getDeviceName()); | ||
148 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
149 | + } | ||
150 | + } | ||
151 | + break; | ||
152 | + } | ||
153 | + throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); | ||
154 | + } | ||
155 | + | ||
156 | + private ProvisionResponse processProvision(Device device, ProvisionRequest provisionRequest) { | ||
157 | + try { | ||
158 | + Optional<AttributeKvEntry> provisionState = attributesService.find(device.getTenantId(), device.getId(), | ||
159 | + DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE).get(); | ||
160 | + if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) { | ||
161 | + notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); | ||
162 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
163 | + } else { | ||
164 | + saveProvisionStateAttribute(device).get(); | ||
165 | + notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); | ||
166 | + } | ||
167 | + } catch (InterruptedException | ExecutionException e) { | ||
168 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
169 | + } | ||
170 | + return new ProvisionResponse(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()), ProvisionResponseStatus.SUCCESS); | ||
171 | + } | ||
172 | + | ||
173 | + private ProvisionResponse createDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { | ||
174 | + deviceCreationLock.lock(); | ||
175 | + try { | ||
176 | + return processCreateDevice(provisionRequest, profile); | ||
177 | + } finally { | ||
178 | + deviceCreationLock.unlock(); | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) { | ||
183 | + pushProvisionEventToRuleEngine(provisionRequest, device, type); | ||
184 | + logAction(device.getTenantId(), device.getCustomerId(), device, success, provisionRequest); | ||
185 | + } | ||
186 | + | ||
187 | + private ProvisionResponse processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { | ||
188 | + Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); | ||
189 | + try { | ||
190 | + if (device == null) { | ||
191 | + Device savedDevice = deviceService.saveDevice(provisionRequest, profile); | ||
192 | + | ||
193 | + deviceStateService.onDeviceAdded(savedDevice); | ||
194 | + saveProvisionStateAttribute(savedDevice).get(); | ||
195 | + pushDeviceCreatedEventToRuleEngine(savedDevice); | ||
196 | + notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true); | ||
197 | + | ||
198 | + return new ProvisionResponse(getDeviceCredentials(savedDevice), ProvisionResponseStatus.SUCCESS); | ||
199 | + } else { | ||
200 | + log.warn("[{}] The device is already provisioned!", device.getName()); | ||
201 | + notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); | ||
202 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
203 | + } | ||
204 | + } catch (InterruptedException | ExecutionException e) { | ||
205 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
206 | + } | ||
207 | + } | ||
208 | + | ||
209 | + private ListenableFuture<List<Void>> saveProvisionStateAttribute(Device device) { | ||
210 | + return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, | ||
211 | + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE), | ||
212 | + System.currentTimeMillis()))); | ||
213 | + } | ||
214 | + | ||
215 | + private DeviceCredentials getDeviceCredentials(Device device) { | ||
216 | + return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); | ||
217 | + } | ||
218 | + | ||
219 | + private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { | ||
220 | + try { | ||
221 | + JsonNode entityNode = JacksonUtil.valueToTree(request); | ||
222 | + TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); | ||
223 | + sendToRuleEngine(device.getTenantId(), msg, null); | ||
224 | + } catch (IllegalArgumentException e) { | ||
225 | + log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); | ||
226 | + } | ||
227 | + } | ||
228 | + | ||
229 | + private void pushDeviceCreatedEventToRuleEngine(Device device) { | ||
230 | + try { | ||
231 | + ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); | ||
232 | + TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); | ||
233 | + sendToRuleEngine(device.getTenantId(), msg, null); | ||
234 | + } catch (JsonProcessingException | IllegalArgumentException e) { | ||
235 | + log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); | ||
236 | + } | ||
237 | + } | ||
238 | + | ||
239 | + protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { | ||
240 | + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator()); | ||
241 | + TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder().setTbMsg(TbMsg.toByteString(tbMsg)) | ||
242 | + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) | ||
243 | + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build(); | ||
244 | + ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback); | ||
245 | + } | ||
246 | + | ||
247 | + private TbMsgMetaData createTbMsgMetaData(Device device) { | ||
248 | + TbMsgMetaData metaData = new TbMsgMetaData(); | ||
249 | + metaData.putValue("tenantId", device.getTenantId().toString()); | ||
250 | + return metaData; | ||
251 | + } | ||
252 | + | ||
253 | + private void logAction(TenantId tenantId, CustomerId customerId, Device device, boolean success, ProvisionRequest provisionRequest) { | ||
254 | + ActionType actionType = success ? ActionType.PROVISION_SUCCESS : ActionType.PROVISION_FAILURE; | ||
255 | + auditLogService.logEntityAction(tenantId, customerId, new UserId(UserId.NULL_UUID), device.getName(), device.getId(), device, actionType, null, provisionRequest); | ||
256 | + } | ||
257 | +} |
@@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture; | @@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture; | ||
23 | import com.google.common.util.concurrent.MoreExecutors; | 23 | import com.google.common.util.concurrent.MoreExecutors; |
24 | import com.google.protobuf.ByteString; | 24 | import com.google.protobuf.ByteString; |
25 | import lombok.extern.slf4j.Slf4j; | 25 | import lombok.extern.slf4j.Slf4j; |
26 | -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
27 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
28 | import org.springframework.util.StringUtils; | 27 | import org.springframework.util.StringUtils; |
29 | import org.thingsboard.server.common.data.DataConstants; | 28 | import org.thingsboard.server.common.data.DataConstants; |
@@ -31,6 +30,8 @@ import org.thingsboard.server.common.data.Device; | @@ -31,6 +30,8 @@ import org.thingsboard.server.common.data.Device; | ||
31 | import org.thingsboard.server.common.data.DeviceProfile; | 30 | import org.thingsboard.server.common.data.DeviceProfile; |
32 | import org.thingsboard.server.common.data.TenantProfile; | 31 | import org.thingsboard.server.common.data.TenantProfile; |
33 | import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; | 32 | import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
33 | +import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; | ||
34 | +import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; | ||
34 | import org.thingsboard.server.common.data.id.CustomerId; | 35 | import org.thingsboard.server.common.data.id.CustomerId; |
35 | import org.thingsboard.server.common.data.id.DeviceId; | 36 | import org.thingsboard.server.common.data.id.DeviceId; |
36 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 37 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
@@ -45,7 +46,10 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; | @@ -45,7 +46,10 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; | ||
45 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | 46 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
46 | import org.thingsboard.server.dao.device.DeviceCredentialsService; | 47 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
47 | import org.thingsboard.server.dao.device.DeviceProfileService; | 48 | import org.thingsboard.server.dao.device.DeviceProfileService; |
49 | +import org.thingsboard.server.dao.device.DeviceProvisionService; | ||
48 | import org.thingsboard.server.dao.device.DeviceService; | 50 | import org.thingsboard.server.dao.device.DeviceService; |
51 | +import org.thingsboard.server.dao.device.provision.ProvisionRequest; | ||
52 | +import org.thingsboard.server.dao.device.provision.ProvisionResponse; | ||
49 | import org.thingsboard.server.dao.relation.RelationService; | 53 | import org.thingsboard.server.dao.relation.RelationService; |
50 | import org.thingsboard.server.dao.tenant.TenantProfileService; | 54 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
51 | import org.thingsboard.server.dao.tenant.TenantService; | 55 | import org.thingsboard.server.dao.tenant.TenantService; |
@@ -56,6 +60,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFro | @@ -56,6 +60,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFro | ||
56 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; | 60 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; |
57 | import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoRequestMsg; | 61 | import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoRequestMsg; |
58 | import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoResponseMsg; | 62 | import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoResponseMsg; |
63 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; | ||
59 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; | 64 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
60 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; | 65 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
61 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; | 66 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; |
@@ -63,6 +68,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenR | @@ -63,6 +68,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenR | ||
63 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 68 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
64 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 69 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
65 | import org.thingsboard.server.queue.util.TbCoreComponent; | 70 | import org.thingsboard.server.queue.util.TbCoreComponent; |
71 | +import org.thingsboard.server.dao.device.provision.ProvisionFailedException; | ||
66 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; | 72 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
67 | import org.thingsboard.server.service.queue.TbClusterService; | 73 | import org.thingsboard.server.service.queue.TbClusterService; |
68 | import org.thingsboard.server.service.state.DeviceStateService; | 74 | import org.thingsboard.server.service.state.DeviceStateService; |
@@ -94,6 +100,7 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -94,6 +100,7 @@ public class DefaultTransportApiService implements TransportApiService { | ||
94 | private final DbCallbackExecutorService dbCallbackExecutorService; | 100 | private final DbCallbackExecutorService dbCallbackExecutorService; |
95 | private final TbClusterService tbClusterService; | 101 | private final TbClusterService tbClusterService; |
96 | private final DataDecodingEncodingService dataDecodingEncodingService; | 102 | private final DataDecodingEncodingService dataDecodingEncodingService; |
103 | + private final DeviceProvisionService deviceProvisionService; | ||
97 | 104 | ||
98 | 105 | ||
99 | private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); | 106 | private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); |
@@ -102,7 +109,8 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -102,7 +109,8 @@ public class DefaultTransportApiService implements TransportApiService { | ||
102 | TenantProfileService tenantProfileService, DeviceService deviceService, | 109 | TenantProfileService tenantProfileService, DeviceService deviceService, |
103 | RelationService relationService, DeviceCredentialsService deviceCredentialsService, | 110 | RelationService relationService, DeviceCredentialsService deviceCredentialsService, |
104 | DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, | 111 | DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, |
105 | - TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService) { | 112 | + TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, |
113 | + DeviceProvisionService deviceProvisionService) { | ||
106 | this.deviceProfileService = deviceProfileService; | 114 | this.deviceProfileService = deviceProfileService; |
107 | this.tenantService = tenantService; | 115 | this.tenantService = tenantService; |
108 | this.tenantProfileService = tenantProfileService; | 116 | this.tenantProfileService = tenantProfileService; |
@@ -113,6 +121,7 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -113,6 +121,7 @@ public class DefaultTransportApiService implements TransportApiService { | ||
113 | this.dbCallbackExecutorService = dbCallbackExecutorService; | 121 | this.dbCallbackExecutorService = dbCallbackExecutorService; |
114 | this.tbClusterService = tbClusterService; | 122 | this.tbClusterService = tbClusterService; |
115 | this.dataDecodingEncodingService = dataDecodingEncodingService; | 123 | this.dataDecodingEncodingService = dataDecodingEncodingService; |
124 | + this.deviceProvisionService = deviceProvisionService; | ||
116 | } | 125 | } |
117 | 126 | ||
118 | @Override | 127 | @Override |
@@ -139,6 +148,9 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -139,6 +148,9 @@ public class DefaultTransportApiService implements TransportApiService { | ||
139 | } else if (transportApiRequestMsg.hasGetDeviceProfileRequestMsg()) { | 148 | } else if (transportApiRequestMsg.hasGetDeviceProfileRequestMsg()) { |
140 | return Futures.transform(handle(transportApiRequestMsg.getGetDeviceProfileRequestMsg()), | 149 | return Futures.transform(handle(transportApiRequestMsg.getGetDeviceProfileRequestMsg()), |
141 | value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | 150 | value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); |
151 | + } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) { | ||
152 | + return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()), | ||
153 | + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | ||
142 | } | 154 | } |
143 | return Futures.transform(getEmptyTransportApiResponseFuture(), | 155 | return Futures.transform(getEmptyTransportApiResponseFuture(), |
144 | value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | 156 | value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); |
@@ -263,6 +275,50 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -263,6 +275,50 @@ public class DefaultTransportApiService implements TransportApiService { | ||
263 | }, dbCallbackExecutorService); | 275 | }, dbCallbackExecutorService); |
264 | } | 276 | } |
265 | 277 | ||
278 | + private ListenableFuture<TransportApiResponseMsg> handle(ProvisionDeviceRequestMsg requestMsg) { | ||
279 | + ListenableFuture<ProvisionResponse> provisionResponseFuture = null; | ||
280 | + try { | ||
281 | + provisionResponseFuture = Futures.immediateFuture(deviceProvisionService.provisionDevice( | ||
282 | + new ProvisionRequest( | ||
283 | + requestMsg.getDeviceName(), | ||
284 | + requestMsg.getCredentialsType() != null ? DeviceCredentialsType.valueOf(requestMsg.getCredentialsType().name()) : null, | ||
285 | + new ProvisionDeviceCredentialsData(requestMsg.getCredentialsDataProto().getValidateDeviceTokenRequestMsg().getToken(), | ||
286 | + requestMsg.getCredentialsDataProto().getValidateBasicMqttCredRequestMsg().getClientId(), | ||
287 | + requestMsg.getCredentialsDataProto().getValidateBasicMqttCredRequestMsg().getUserName(), | ||
288 | + requestMsg.getCredentialsDataProto().getValidateBasicMqttCredRequestMsg().getPassword(), | ||
289 | + requestMsg.getCredentialsDataProto().getValidateDeviceX509CertRequestMsg().getHash()), | ||
290 | + new ProvisionDeviceProfileCredentials( | ||
291 | + requestMsg.getProvisionDeviceCredentialsMsg().getProvisionDeviceKey(), | ||
292 | + requestMsg.getProvisionDeviceCredentialsMsg().getProvisionDeviceSecret())))); | ||
293 | + } catch (ProvisionFailedException e) { | ||
294 | + return Futures.immediateFuture(getTransportApiResponseMsg( | ||
295 | + TransportProtos.DeviceCredentialsProto.getDefaultInstance(), | ||
296 | + TransportProtos.ProvisionResponseStatus.valueOf(e.getMessage()))); | ||
297 | + } | ||
298 | + return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg( | ||
299 | + getDeviceCredentials(provisionResponse.getDeviceCredentials()), TransportProtos.ProvisionResponseStatus.SUCCESS), | ||
300 | + dbCallbackExecutorService); | ||
301 | + } | ||
302 | + | ||
303 | + private TransportApiResponseMsg getTransportApiResponseMsg(TransportProtos.DeviceCredentialsProto deviceCredentials, TransportProtos.ProvisionResponseStatus status) { | ||
304 | + return TransportApiResponseMsg.newBuilder() | ||
305 | + .setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder() | ||
306 | + .setDeviceCredentials(deviceCredentials) | ||
307 | + .setProvisionResponseStatus(status) | ||
308 | + .build()) | ||
309 | + .build(); | ||
310 | + } | ||
311 | + | ||
312 | + private TransportProtos.DeviceCredentialsProto getDeviceCredentials(DeviceCredentials deviceCredentials) { | ||
313 | + return TransportProtos.DeviceCredentialsProto.newBuilder() | ||
314 | + .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) | ||
315 | + .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()) | ||
316 | + .setCredentialsType(TransportProtos.CredentialsType.valueOf(deviceCredentials.getCredentialsType().name())) | ||
317 | + .setCredentialsId(deviceCredentials.getCredentialsId()) | ||
318 | + .setCredentialsValue(deviceCredentials.getCredentialsValue() != null ? deviceCredentials.getCredentialsValue() : "") | ||
319 | + .build(); | ||
320 | + } | ||
321 | + | ||
266 | private ListenableFuture<TransportApiResponseMsg> handle(GetTenantRoutingInfoRequestMsg requestMsg) { | 322 | private ListenableFuture<TransportApiResponseMsg> handle(GetTenantRoutingInfoRequestMsg requestMsg) { |
267 | TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); | 323 | TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); |
268 | // TODO: Tenant Profile from cache | 324 | // TODO: Tenant Profile from cache |
@@ -68,6 +68,7 @@ import org.thingsboard.server.common.data.User; | @@ -68,6 +68,7 @@ import org.thingsboard.server.common.data.User; | ||
68 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | 68 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
69 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; | 69 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; |
70 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | 70 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
71 | +import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; | ||
71 | import org.thingsboard.server.common.data.id.HasId; | 72 | import org.thingsboard.server.common.data.id.HasId; |
72 | import org.thingsboard.server.common.data.id.RuleChainId; | 73 | import org.thingsboard.server.common.data.id.RuleChainId; |
73 | import org.thingsboard.server.common.data.id.TenantId; | 74 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.DeviceProfileType; | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.DeviceProfileType; | ||
28 | import org.thingsboard.server.common.data.DeviceTransportType; | 28 | import org.thingsboard.server.common.data.DeviceTransportType; |
29 | import org.thingsboard.server.common.data.Tenant; | 29 | import org.thingsboard.server.common.data.Tenant; |
30 | import org.thingsboard.server.common.data.User; | 30 | import org.thingsboard.server.common.data.User; |
31 | +import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; | ||
31 | import org.thingsboard.server.common.data.page.PageData; | 32 | import org.thingsboard.server.common.data.page.PageData; |
32 | import org.thingsboard.server.common.data.page.PageLink; | 33 | import org.thingsboard.server.common.data.page.PageLink; |
33 | import org.thingsboard.server.common.data.security.Authority; | 34 | import org.thingsboard.server.common.data.security.Authority; |
@@ -153,6 +154,17 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController | @@ -153,6 +154,17 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController | ||
153 | .andExpect(statusReason(containsString("Device profile with such name already exists"))); | 154 | .andExpect(statusReason(containsString("Device profile with such name already exists"))); |
154 | } | 155 | } |
155 | 156 | ||
157 | + @Test | ||
158 | + public void testSaveDeviceProfileWithSameProvisionDeviceKey() throws Exception { | ||
159 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | ||
160 | + deviceProfile.setProvisionDeviceKey("testProvisionDeviceKey"); | ||
161 | + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk()); | ||
162 | + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2"); | ||
163 | + deviceProfile2.setProvisionDeviceKey("testProvisionDeviceKey"); | ||
164 | + doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest()) | ||
165 | + .andExpect(statusReason(containsString("Device profile with such provision device key already exists"))); | ||
166 | + } | ||
167 | + | ||
156 | @Ignore | 168 | @Ignore |
157 | @Test | 169 | @Test |
158 | public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception { | 170 | public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception { |
@@ -26,13 +26,18 @@ import org.junit.Assert; | @@ -26,13 +26,18 @@ import org.junit.Assert; | ||
26 | import org.springframework.util.StringUtils; | 26 | import org.springframework.util.StringUtils; |
27 | import org.thingsboard.server.common.data.Device; | 27 | import org.thingsboard.server.common.data.Device; |
28 | import org.thingsboard.server.common.data.DeviceProfile; | 28 | import org.thingsboard.server.common.data.DeviceProfile; |
29 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
29 | import org.thingsboard.server.common.data.DeviceProfileType; | 30 | import org.thingsboard.server.common.data.DeviceProfileType; |
30 | import org.thingsboard.server.common.data.DeviceTransportType; | 31 | import org.thingsboard.server.common.data.DeviceTransportType; |
31 | import org.thingsboard.server.common.data.Tenant; | 32 | import org.thingsboard.server.common.data.Tenant; |
32 | import org.thingsboard.server.common.data.TransportPayloadType; | 33 | import org.thingsboard.server.common.data.TransportPayloadType; |
33 | import org.thingsboard.server.common.data.User; | 34 | import org.thingsboard.server.common.data.User; |
35 | +import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; | ||
36 | +import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration; | ||
34 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | 37 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
35 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | 38 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
39 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileProvisionConfiguration; | ||
40 | +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; | ||
36 | import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | 41 | import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
37 | import org.thingsboard.server.common.data.security.Authority; | 42 | import org.thingsboard.server.common.data.security.Authority; |
38 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 43 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
@@ -64,7 +69,18 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -64,7 +69,18 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
64 | protected Device savedGateway; | 69 | protected Device savedGateway; |
65 | protected String gatewayAccessToken; | 70 | protected String gatewayAccessToken; |
66 | 71 | ||
67 | - protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { | 72 | + protected void processBeforeTest (String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { |
73 | + this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, DeviceProfileProvisionType.DISABLED, null, null); | ||
74 | + } | ||
75 | + | ||
76 | + protected void processBeforeTest(String deviceName, | ||
77 | + String gatewayName, | ||
78 | + TransportPayloadType payloadType, | ||
79 | + String telemetryTopic, | ||
80 | + String attributesTopic, | ||
81 | + DeviceProfileProvisionType provisionType, | ||
82 | + String provisionKey, String provisionSecret | ||
83 | + ) throws Exception { | ||
68 | loginSysAdmin(); | 84 | loginSysAdmin(); |
69 | 85 | ||
70 | Tenant tenant = new Tenant(); | 86 | Tenant tenant = new Tenant(); |
@@ -93,7 +109,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -93,7 +109,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
93 | gateway.setAdditionalInfo(additionalInfo); | 109 | gateway.setAdditionalInfo(additionalInfo); |
94 | 110 | ||
95 | if (payloadType != null) { | 111 | if (payloadType != null) { |
96 | - DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic); | 112 | + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, provisionType, provisionKey, provisionSecret); |
97 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); | 113 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); |
98 | device.setType(savedDeviceProfile.getName()); | 114 | device.setType(savedDeviceProfile.getName()); |
99 | device.setDeviceProfileId(savedDeviceProfile.getId()); | 115 | device.setDeviceProfileId(savedDeviceProfile.getId()); |
@@ -183,11 +199,17 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -183,11 +199,17 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
183 | return keyValueProtoBuilder.build(); | 199 | return keyValueProtoBuilder.build(); |
184 | } | 200 | } |
185 | 201 | ||
186 | - protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) { | 202 | + protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, |
203 | + String telemetryTopic, String attributesTopic, | ||
204 | + DeviceProfileProvisionType provisionType, | ||
205 | + String provisionKey, String provisionSecret | ||
206 | + ) { | ||
187 | DeviceProfile deviceProfile = new DeviceProfile(); | 207 | DeviceProfile deviceProfile = new DeviceProfile(); |
188 | deviceProfile.setName(transportPayloadType.name()); | 208 | deviceProfile.setName(transportPayloadType.name()); |
189 | deviceProfile.setType(DeviceProfileType.DEFAULT); | 209 | deviceProfile.setType(DeviceProfileType.DEFAULT); |
190 | deviceProfile.setTransportType(DeviceTransportType.MQTT); | 210 | deviceProfile.setTransportType(DeviceTransportType.MQTT); |
211 | + deviceProfile.setProvisionType(provisionType); | ||
212 | + deviceProfile.setProvisionDeviceKey(provisionKey); | ||
191 | deviceProfile.setDescription(transportPayloadType.name() + " Test"); | 213 | deviceProfile.setDescription(transportPayloadType.name() + " Test"); |
192 | DeviceProfileData deviceProfileData = new DeviceProfileData(); | 214 | DeviceProfileData deviceProfileData = new DeviceProfileData(); |
193 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | 215 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
@@ -200,6 +222,19 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -200,6 +222,19 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
200 | transportConfiguration.setDeviceAttributesTopic(attributesTopic); | 222 | transportConfiguration.setDeviceAttributesTopic(attributesTopic); |
201 | } | 223 | } |
202 | deviceProfileData.setTransportConfiguration(transportConfiguration); | 224 | deviceProfileData.setTransportConfiguration(transportConfiguration); |
225 | + DeviceProfileProvisionConfiguration provisionConfiguration; | ||
226 | + switch (provisionType) { | ||
227 | + case ALLOW_CREATE_NEW_DEVICES: | ||
228 | + provisionConfiguration = new AllowCreateNewDevicesDeviceProfileProvisionConfiguration(provisionSecret); | ||
229 | + break; | ||
230 | + case CHECK_PRE_PROVISIONED_DEVICES: | ||
231 | + provisionConfiguration = new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(provisionSecret); | ||
232 | + break; | ||
233 | + case DISABLED: | ||
234 | + default: | ||
235 | + provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(provisionSecret); | ||
236 | + } | ||
237 | + deviceProfileData.setProvisionConfiguration(provisionConfiguration); | ||
203 | deviceProfileData.setConfiguration(configuration); | 238 | deviceProfileData.setConfiguration(configuration); |
204 | deviceProfile.setProfileData(deviceProfileData); | 239 | deviceProfile.setProfileData(deviceProfileData); |
205 | deviceProfile.setDefault(false); | 240 | deviceProfile.setDefault(false); |
@@ -31,7 +31,8 @@ import java.util.Arrays; | @@ -31,7 +31,8 @@ import java.util.Arrays; | ||
31 | "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", | 31 | "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", |
32 | "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", | 32 | "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", |
33 | "org.thingsboard.server.mqtt.attributes.request.sql.*Test", | 33 | "org.thingsboard.server.mqtt.attributes.request.sql.*Test", |
34 | - "org.thingsboard.server.mqtt.claim.sql.*Test" | 34 | + "org.thingsboard.server.mqtt.claim.sql.*Test", |
35 | + "org.thingsboard.server.mqtt.provision.sql.*Test" | ||
35 | }) | 36 | }) |
36 | public class MqttSqlTestSuite { | 37 | public class MqttSqlTestSuite { |
37 | 38 |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.provision; | ||
17 | + | ||
18 | +import com.google.gson.JsonObject; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
25 | +import org.junit.After; | ||
26 | +import org.junit.Assert; | ||
27 | +import org.junit.Test; | ||
28 | +import org.springframework.beans.factory.annotation.Autowired; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
31 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
32 | +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; | ||
33 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
34 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
35 | +import org.thingsboard.server.common.msg.EncryptionUtil; | ||
36 | +import org.thingsboard.server.common.transport.util.JsonUtils; | ||
37 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | ||
38 | +import org.thingsboard.server.dao.device.DeviceService; | ||
39 | +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; | ||
40 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
41 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
42 | + | ||
43 | +import java.util.concurrent.CountDownLatch; | ||
44 | +import java.util.concurrent.TimeUnit; | ||
45 | + | ||
46 | +@Slf4j | ||
47 | +public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest { | ||
48 | + | ||
49 | + @Autowired | ||
50 | + DeviceCredentialsService deviceCredentialsService; | ||
51 | + | ||
52 | + @Autowired | ||
53 | + DeviceService deviceService; | ||
54 | + | ||
55 | + @After | ||
56 | + public void afterTest() throws Exception { | ||
57 | + super.processAfterTest(); | ||
58 | + } | ||
59 | + | ||
60 | + @Test | ||
61 | + public void testProvisioningDisabledDevice() throws Exception { | ||
62 | + processTestProvisioningDisabledDevice(); | ||
63 | + } | ||
64 | + | ||
65 | + @Test | ||
66 | + public void testProvisioningCheckPreProvisionedDevice() throws Exception { | ||
67 | + processTestProvisioningCheckPreProvisionedDevice(); | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + public void testProvisioningCreateNewDeviceWithoutCredentials() throws Exception { | ||
72 | + processTestProvisioningCreateNewDeviceWithoutCredentials(); | ||
73 | + } | ||
74 | + | ||
75 | + @Test | ||
76 | + public void testProvisioningCreateNewDeviceWithAccessToken() throws Exception { | ||
77 | + processTestProvisioningCreateNewDeviceWithAccessToken(); | ||
78 | + } | ||
79 | + | ||
80 | + @Test | ||
81 | + public void testProvisioningCreateNewDeviceWithCert() throws Exception { | ||
82 | + processTestProvisioningCreateNewDeviceWithCert(); | ||
83 | + } | ||
84 | + | ||
85 | + @Test | ||
86 | + public void testProvisioningCreateNewDeviceWithMqttBasic() throws Exception { | ||
87 | + processTestProvisioningCreateNewDeviceWithMqttBasic(); | ||
88 | + } | ||
89 | + | ||
90 | + @Test | ||
91 | + public void testProvisioningWithBadKeyDevice() throws Exception { | ||
92 | + processTestProvisioningWithBadKeyDevice(); | ||
93 | + } | ||
94 | + | ||
95 | + | ||
96 | + protected void processTestProvisioningDisabledDevice() throws Exception { | ||
97 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.DISABLED, null, null); | ||
98 | + byte[] result = createMqttClientAndPublish().getPayloadBytes(); | ||
99 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
100 | + Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); | ||
101 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("provisionDeviceStatus").getAsString()); | ||
102 | + } | ||
103 | + | ||
104 | + | ||
105 | + protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { | ||
106 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
107 | + byte[] result = createMqttClientAndPublish().getPayloadBytes(); | ||
108 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
109 | + | ||
110 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
111 | + | ||
112 | + Assert.assertNotNull(createdDevice); | ||
113 | + Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | ||
114 | + | ||
115 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
116 | + | ||
117 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); | ||
118 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | ||
119 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | ||
120 | + } | ||
121 | + | ||
122 | + | ||
123 | + protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { | ||
124 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
125 | + String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\""; | ||
126 | + byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); | ||
127 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
128 | + | ||
129 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
130 | + | ||
131 | + Assert.assertNotNull(createdDevice); | ||
132 | + Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | ||
133 | + | ||
134 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
135 | + | ||
136 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); | ||
137 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | ||
138 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "ACCESS_TOKEN"); | ||
139 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); | ||
140 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | ||
141 | + } | ||
142 | + | ||
143 | + | ||
144 | + protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { | ||
145 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
146 | + String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\""; | ||
147 | + byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); | ||
148 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
149 | + | ||
150 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
151 | + | ||
152 | + Assert.assertNotNull(createdDevice); | ||
153 | + Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | ||
154 | + | ||
155 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
156 | + | ||
157 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); | ||
158 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | ||
159 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "X509_CERTIFICATE"); | ||
160 | + | ||
161 | + String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); | ||
162 | + String sha3Hash = EncryptionUtil.getSha3Hash(cert); | ||
163 | + | ||
164 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); | ||
165 | + | ||
166 | + Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); | ||
167 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | ||
168 | + } | ||
169 | + | ||
170 | + | ||
171 | + protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { | ||
172 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
173 | + String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\""; | ||
174 | + byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); | ||
175 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
176 | + | ||
177 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
178 | + | ||
179 | + Assert.assertNotNull(createdDevice); | ||
180 | + Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | ||
181 | + | ||
182 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
183 | + | ||
184 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); | ||
185 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | ||
186 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "MQTT_BASIC"); | ||
187 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username")); | ||
188 | + | ||
189 | + BasicMqttCredentials mqttCredentials = new BasicMqttCredentials(); | ||
190 | + mqttCredentials.setClientId("test_clientId"); | ||
191 | + mqttCredentials.setUserName("test_username"); | ||
192 | + mqttCredentials.setPassword("test_password"); | ||
193 | + | ||
194 | + Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials)); | ||
195 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | ||
196 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | ||
197 | + } | ||
198 | + | ||
199 | + protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { | ||
200 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
201 | + byte[] result = createMqttClientAndPublish().getPayloadBytes(); | ||
202 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
203 | + Assert.assertEquals(savedDevice.getId().toString(), response.get("deviceId").getAsString()); | ||
204 | + | ||
205 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); | ||
206 | + | ||
207 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); | ||
208 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | ||
209 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | ||
210 | + } | ||
211 | + | ||
212 | + protected void processTestProvisioningWithBadKeyDevice() throws Exception { | ||
213 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); | ||
214 | + byte[] result = createMqttClientAndPublish().getPayloadBytes(); | ||
215 | + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); | ||
216 | + Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); | ||
217 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("provisionDeviceStatus").getAsString()); | ||
218 | + } | ||
219 | + | ||
220 | + protected TestMqttCallback createMqttClientAndPublish() throws Exception { | ||
221 | + return createMqttClientAndPublish(""); | ||
222 | + } | ||
223 | + | ||
224 | + protected TestMqttCallback createMqttClientAndPublish(String deviceCredentials) throws Exception { | ||
225 | + String provisionRequestMsg = createTestProvisionMessage(deviceCredentials); | ||
226 | + MqttAsyncClient client = getMqttAsyncClient("provision"); | ||
227 | + TestMqttCallback onProvisionCallback = getTestMqttCallback(); | ||
228 | + client.setCallback(onProvisionCallback); | ||
229 | + client.subscribe(MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
230 | + Thread.sleep(2000); | ||
231 | + client.publish(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC, new MqttMessage(provisionRequestMsg.getBytes())); | ||
232 | + onProvisionCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
233 | + return onProvisionCallback; | ||
234 | + } | ||
235 | + | ||
236 | + | ||
237 | + protected TestMqttCallback getTestMqttCallback() { | ||
238 | + CountDownLatch latch = new CountDownLatch(1); | ||
239 | + return new TestMqttCallback(latch); | ||
240 | + } | ||
241 | + | ||
242 | + | ||
243 | + protected static class TestMqttCallback implements MqttCallback { | ||
244 | + | ||
245 | + private final CountDownLatch latch; | ||
246 | + private Integer qoS; | ||
247 | + private byte[] payloadBytes; | ||
248 | + | ||
249 | + TestMqttCallback(CountDownLatch latch) { | ||
250 | + this.latch = latch; | ||
251 | + } | ||
252 | + | ||
253 | + public int getQoS() { | ||
254 | + return qoS; | ||
255 | + } | ||
256 | + | ||
257 | + public byte[] getPayloadBytes() { | ||
258 | + return payloadBytes; | ||
259 | + } | ||
260 | + | ||
261 | + public CountDownLatch getLatch() { | ||
262 | + return latch; | ||
263 | + } | ||
264 | + | ||
265 | + @Override | ||
266 | + public void connectionLost(Throwable throwable) { | ||
267 | + } | ||
268 | + | ||
269 | + @Override | ||
270 | + public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { | ||
271 | + qoS = mqttMessage.getQos(); | ||
272 | + payloadBytes = mqttMessage.getPayload(); | ||
273 | + latch.countDown(); | ||
274 | + } | ||
275 | + | ||
276 | + @Override | ||
277 | + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
278 | + | ||
279 | + } | ||
280 | + } | ||
281 | + | ||
282 | + protected String createTestProvisionMessage(String deviceCredentials) { | ||
283 | + return "{\"deviceName\":\"Test Provision device\",\"provisionDeviceKey\":\"testProvisionKey\", \"provisionDeviceSecret\":\"testProvisionSecret\"" + deviceCredentials + "}"; | ||
284 | + } | ||
285 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.provision; | ||
17 | + | ||
18 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
24 | +import org.junit.After; | ||
25 | +import org.junit.Assert; | ||
26 | +import org.junit.Test; | ||
27 | +import org.springframework.beans.factory.annotation.Autowired; | ||
28 | +import org.thingsboard.server.common.data.Device; | ||
29 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
30 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
31 | +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; | ||
32 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
33 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
34 | +import org.thingsboard.server.common.data.security.DeviceCredentialsType; | ||
35 | +import org.thingsboard.server.common.msg.EncryptionUtil; | ||
36 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | ||
37 | +import org.thingsboard.server.dao.device.DeviceService; | ||
38 | +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; | ||
39 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
40 | +import org.thingsboard.server.gen.transport.TransportProtos.CredentialsDataProto; | ||
41 | +import org.thingsboard.server.gen.transport.TransportProtos.CredentialsType; | ||
42 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceCredentialsMsg; | ||
43 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; | ||
44 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
45 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; | ||
46 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | ||
47 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | ||
48 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
49 | + | ||
50 | +import java.util.UUID; | ||
51 | +import java.util.concurrent.CountDownLatch; | ||
52 | +import java.util.concurrent.TimeUnit; | ||
53 | + | ||
54 | +@Slf4j | ||
55 | +public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest { | ||
56 | + | ||
57 | + @Autowired | ||
58 | + DeviceCredentialsService deviceCredentialsService; | ||
59 | + | ||
60 | + @Autowired | ||
61 | + DeviceService deviceService; | ||
62 | + | ||
63 | + @After | ||
64 | + public void afterTest() throws Exception { | ||
65 | + super.processAfterTest(); | ||
66 | + } | ||
67 | + | ||
68 | + @Test | ||
69 | + public void testProvisioningDisabledDevice() throws Exception { | ||
70 | + processTestProvisioningDisabledDevice(); | ||
71 | + } | ||
72 | + | ||
73 | + @Test | ||
74 | + public void testProvisioningCheckPreProvisionedDevice() throws Exception { | ||
75 | + processTestProvisioningCheckPreProvisionedDevice(); | ||
76 | + } | ||
77 | + | ||
78 | + @Test | ||
79 | + public void testProvisioningCreateNewDeviceWithoutCredentials() throws Exception { | ||
80 | + processTestProvisioningCreateNewDeviceWithoutCredentials(); | ||
81 | + } | ||
82 | + | ||
83 | + @Test | ||
84 | + public void testProvisioningCreateNewDeviceWithAccessToken() throws Exception { | ||
85 | + processTestProvisioningCreateNewDeviceWithAccessToken(); | ||
86 | + } | ||
87 | + | ||
88 | + @Test | ||
89 | + public void testProvisioningCreateNewDeviceWithCert() throws Exception { | ||
90 | + processTestProvisioningCreateNewDeviceWithCert(); | ||
91 | + } | ||
92 | + | ||
93 | + @Test | ||
94 | + public void testProvisioningCreateNewDeviceWithMqttBasic() throws Exception { | ||
95 | + processTestProvisioningCreateNewDeviceWithMqttBasic(); | ||
96 | + } | ||
97 | + | ||
98 | + @Test | ||
99 | + public void testProvisioningWithBadKeyDevice() throws Exception { | ||
100 | + processTestProvisioningWithBadKeyDevice(); | ||
101 | + } | ||
102 | + | ||
103 | + | ||
104 | + protected void processTestProvisioningDisabledDevice() throws Exception { | ||
105 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.DISABLED, null, null); | ||
106 | + ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); | ||
107 | + Assert.assertNotNull(result); | ||
108 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getProvisionResponseStatus().toString()); | ||
109 | + } | ||
110 | + | ||
111 | + protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { | ||
112 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
113 | + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); | ||
114 | + | ||
115 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
116 | + | ||
117 | + Assert.assertNotNull(createdDevice); | ||
118 | + Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | ||
119 | + | ||
120 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
121 | + | ||
122 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | ||
123 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | ||
124 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | ||
125 | + } | ||
126 | + | ||
127 | + protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { | ||
128 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
129 | + CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build(); | ||
130 | + | ||
131 | + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes()); | ||
132 | + | ||
133 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
134 | + | ||
135 | + Assert.assertNotNull(createdDevice); | ||
136 | + Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | ||
137 | + | ||
138 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
139 | + | ||
140 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | ||
141 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | ||
142 | + Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.ACCESS_TOKEN); | ||
143 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); | ||
144 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | ||
145 | + } | ||
146 | + | ||
147 | + protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { | ||
148 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
149 | + CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build(); | ||
150 | + | ||
151 | + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes()); | ||
152 | + | ||
153 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
154 | + | ||
155 | + Assert.assertNotNull(createdDevice); | ||
156 | + Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | ||
157 | + | ||
158 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
159 | + | ||
160 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | ||
161 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | ||
162 | + Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.X509_CERTIFICATE); | ||
163 | + | ||
164 | + String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); | ||
165 | + String sha3Hash = EncryptionUtil.getSha3Hash(cert); | ||
166 | + | ||
167 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); | ||
168 | + | ||
169 | + Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); | ||
170 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | ||
171 | + } | ||
172 | + | ||
173 | + protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { | ||
174 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
175 | + CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateBasicMqttCredRequestMsg( | ||
176 | + ValidateBasicMqttCredRequestMsg.newBuilder() | ||
177 | + .setClientId("test_clientId") | ||
178 | + .setUserName("test_username") | ||
179 | + .setPassword("test_password") | ||
180 | + .build() | ||
181 | + ).build(); | ||
182 | + | ||
183 | + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.MQTT_BASIC, requestCredentials)).getPayloadBytes()); | ||
184 | + | ||
185 | + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); | ||
186 | + | ||
187 | + Assert.assertNotNull(createdDevice); | ||
188 | + Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | ||
189 | + | ||
190 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); | ||
191 | + | ||
192 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | ||
193 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | ||
194 | + Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.MQTT_BASIC); | ||
195 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username")); | ||
196 | + | ||
197 | + BasicMqttCredentials mqttCredentials = new BasicMqttCredentials(); | ||
198 | + mqttCredentials.setClientId("test_clientId"); | ||
199 | + mqttCredentials.setUserName("test_username"); | ||
200 | + mqttCredentials.setPassword("test_password"); | ||
201 | + | ||
202 | + Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials)); | ||
203 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | ||
204 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | ||
205 | + } | ||
206 | + | ||
207 | + protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { | ||
208 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); | ||
209 | + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); | ||
210 | + Assert.assertEquals(savedDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | ||
211 | + | ||
212 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); | ||
213 | + | ||
214 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | ||
215 | + Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | ||
216 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | ||
217 | + } | ||
218 | + | ||
219 | + protected void processTestProvisioningWithBadKeyDevice() throws Exception { | ||
220 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); | ||
221 | + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); | ||
222 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getProvisionResponseStatus().toString()); | ||
223 | + } | ||
224 | + | ||
225 | + protected TestMqttCallback createMqttClientAndPublish() throws Exception { | ||
226 | + byte[] provisionRequestMsg = createTestProvisionMessage(); | ||
227 | + return createMqttClientAndPublish(provisionRequestMsg); | ||
228 | + } | ||
229 | + | ||
230 | + protected TestMqttCallback createMqttClientAndPublish(byte[] provisionRequestMsg) throws Exception { | ||
231 | + MqttAsyncClient client = getMqttAsyncClient("provision"); | ||
232 | + TestMqttCallback onProvisionCallback = getTestMqttCallback(); | ||
233 | + client.setCallback(onProvisionCallback); | ||
234 | + client.subscribe(MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
235 | + Thread.sleep(2000); | ||
236 | + client.publish(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC, new MqttMessage(provisionRequestMsg)); | ||
237 | + onProvisionCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
238 | + return onProvisionCallback; | ||
239 | + } | ||
240 | + | ||
241 | + | ||
242 | + protected TestMqttCallback getTestMqttCallback() { | ||
243 | + CountDownLatch latch = new CountDownLatch(1); | ||
244 | + return new TestMqttCallback(latch); | ||
245 | + } | ||
246 | + | ||
247 | + | ||
248 | + protected static class TestMqttCallback implements MqttCallback { | ||
249 | + | ||
250 | + private final CountDownLatch latch; | ||
251 | + private Integer qoS; | ||
252 | + private byte[] payloadBytes; | ||
253 | + | ||
254 | + TestMqttCallback(CountDownLatch latch) { | ||
255 | + this.latch = latch; | ||
256 | + } | ||
257 | + | ||
258 | + public int getQoS() { | ||
259 | + return qoS; | ||
260 | + } | ||
261 | + | ||
262 | + public byte[] getPayloadBytes() { | ||
263 | + return payloadBytes; | ||
264 | + } | ||
265 | + | ||
266 | + public CountDownLatch getLatch() { | ||
267 | + return latch; | ||
268 | + } | ||
269 | + | ||
270 | + @Override | ||
271 | + public void connectionLost(Throwable throwable) { | ||
272 | + } | ||
273 | + | ||
274 | + @Override | ||
275 | + public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { | ||
276 | + qoS = mqttMessage.getQos(); | ||
277 | + payloadBytes = mqttMessage.getPayload(); | ||
278 | + latch.countDown(); | ||
279 | + } | ||
280 | + | ||
281 | + @Override | ||
282 | + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
283 | + | ||
284 | + } | ||
285 | + } | ||
286 | + | ||
287 | + protected byte[] createTestsProvisionMessage(CredentialsType credentialsType, CredentialsDataProto credentialsData) throws Exception { | ||
288 | + return ProvisionDeviceRequestMsg.newBuilder() | ||
289 | + .setDeviceName("Test Provision device") | ||
290 | + .setCredentialsType(credentialsType != null ? credentialsType : CredentialsType.ACCESS_TOKEN) | ||
291 | + .setCredentialsDataProto(credentialsData != null ? credentialsData: CredentialsDataProto.newBuilder().build()) | ||
292 | + .setProvisionDeviceCredentialsMsg( | ||
293 | + ProvisionDeviceCredentialsMsg.newBuilder() | ||
294 | + .setProvisionDeviceKey("testProvisionKey") | ||
295 | + .setProvisionDeviceSecret("testProvisionSecret") | ||
296 | + ).build() | ||
297 | + .toByteArray(); | ||
298 | + } | ||
299 | + | ||
300 | + | ||
301 | + protected byte[] createTestProvisionMessage() throws Exception { | ||
302 | + return createTestsProvisionMessage(null, null); | ||
303 | + } | ||
304 | + | ||
305 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.provision.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.provision.AbstractMqttProvisionJsonDeviceTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttProvisionDeviceJsonSqlTest extends AbstractMqttProvisionJsonDeviceTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.provision.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.provision.AbstractMqttProvisionProtoDeviceTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttProvisionDeviceProtoSqlTest extends AbstractMqttProvisionProtoDeviceTest { | ||
23 | +} |
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | +import org.thingsboard.server.common.data.Device; | ||
20 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
21 | +import org.thingsboard.server.dao.device.provision.ProvisionFailedException; | ||
22 | +import org.thingsboard.server.dao.device.provision.ProvisionRequest; | ||
23 | +import org.thingsboard.server.dao.device.provision.ProvisionResponse; | ||
24 | + | ||
25 | +public interface DeviceProvisionService { | ||
26 | + | ||
27 | + ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) throws ProvisionFailedException; | ||
28 | +} |
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.device; | @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.device; | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.Device; | 19 | import org.thingsboard.server.common.data.Device; |
20 | import org.thingsboard.server.common.data.DeviceInfo; | 20 | import org.thingsboard.server.common.data.DeviceInfo; |
21 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
21 | import org.thingsboard.server.common.data.EntitySubtype; | 22 | import org.thingsboard.server.common.data.EntitySubtype; |
22 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; | 23 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
23 | import org.thingsboard.server.common.data.id.CustomerId; | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
@@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | ||
26 | import org.thingsboard.server.common.data.id.TenantId; | 27 | import org.thingsboard.server.common.data.id.TenantId; |
27 | import org.thingsboard.server.common.data.page.PageData; | 28 | import org.thingsboard.server.common.data.page.PageData; |
28 | import org.thingsboard.server.common.data.page.PageLink; | 29 | import org.thingsboard.server.common.data.page.PageLink; |
30 | +import org.thingsboard.server.dao.device.provision.ProvisionRequest; | ||
29 | 31 | ||
30 | import java.util.List; | 32 | import java.util.List; |
31 | 33 | ||
@@ -83,4 +85,6 @@ public interface DeviceService { | @@ -83,4 +85,6 @@ public interface DeviceService { | ||
83 | 85 | ||
84 | Device assignDeviceToTenant(TenantId tenantId, Device device); | 86 | Device assignDeviceToTenant(TenantId tenantId, Device device); |
85 | 87 | ||
88 | + Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile); | ||
89 | + | ||
86 | } | 90 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device.provision; | ||
17 | + | ||
18 | +public class ProvisionFailedException extends RuntimeException { | ||
19 | + public ProvisionFailedException(String errorMsg) { | ||
20 | + super(errorMsg); | ||
21 | + } | ||
22 | +} |
common/dao-api/src/main/java/org/thingsboard/server/dao/device/provision/ProvisionRequest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device.provision; | ||
17 | + | ||
18 | +import lombok.AllArgsConstructor; | ||
19 | +import lombok.Data; | ||
20 | +import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; | ||
21 | +import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; | ||
22 | +import org.thingsboard.server.common.data.security.DeviceCredentialsType; | ||
23 | + | ||
24 | +@Data | ||
25 | +@AllArgsConstructor | ||
26 | +public class ProvisionRequest { | ||
27 | + private String deviceName; | ||
28 | + private DeviceCredentialsType credentialsType; | ||
29 | + private ProvisionDeviceCredentialsData credentialsData; | ||
30 | + private ProvisionDeviceProfileCredentials credentials; | ||
31 | +} |
common/dao-api/src/main/java/org/thingsboard/server/dao/device/provision/ProvisionResponse.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device.provision; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
20 | + | ||
21 | +@Data | ||
22 | +public class ProvisionResponse { | ||
23 | + private final DeviceCredentials deviceCredentials; | ||
24 | + private final ProvisionResponseStatus responseStatus; | ||
25 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device.provision; | ||
17 | + | ||
18 | +public enum ProvisionResponseStatus { | ||
19 | + UNKNOWN, | ||
20 | + SUCCESS, | ||
21 | + NOT_FOUND, | ||
22 | + FAILURE | ||
23 | +} |
@@ -63,6 +63,8 @@ public class DataConstants { | @@ -63,6 +63,8 @@ public class DataConstants { | ||
63 | public static final String ALARM_CLEAR = "ALARM_CLEAR"; | 63 | public static final String ALARM_CLEAR = "ALARM_CLEAR"; |
64 | public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT"; | 64 | public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT"; |
65 | public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT"; | 65 | public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT"; |
66 | + public static final String PROVISION_SUCCESS = "PROVISION_SUCCESS"; | ||
67 | + public static final String PROVISION_FAILURE = "PROVISION_FAILURE"; | ||
66 | 68 | ||
67 | public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; | 69 | public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; |
68 | 70 | ||
@@ -70,4 +72,18 @@ public class DataConstants { | @@ -70,4 +72,18 @@ public class DataConstants { | ||
70 | public static final String SECRET_KEY_FIELD_NAME = "secretKey"; | 72 | public static final String SECRET_KEY_FIELD_NAME = "secretKey"; |
71 | public static final String DURATION_MS_FIELD_NAME = "durationMs"; | 73 | public static final String DURATION_MS_FIELD_NAME = "durationMs"; |
72 | 74 | ||
75 | + public static final String PROVISION = "provision"; | ||
76 | + public static final String PROVISION_KEY = "provisionDeviceKey"; | ||
77 | + public static final String PROVISION_SECRET = "provisionDeviceSecret"; | ||
78 | + | ||
79 | + public static final String DEVICE_NAME = "deviceName"; | ||
80 | + public static final String DEVICE_TYPE = "deviceType"; | ||
81 | + public static final String CERT_PUB_KEY = "x509CertPubKey"; | ||
82 | + public static final String CREDENTIALS_TYPE = "credentialsType"; | ||
83 | + public static final String TOKEN = "token"; | ||
84 | + public static final String HASH = "hash"; | ||
85 | + public static final String CLIENT_ID = "clientId"; | ||
86 | + public static final String USERNAME = "username"; | ||
87 | + public static final String PASSWORD = "password"; | ||
88 | + | ||
73 | } | 89 | } |
@@ -41,10 +41,12 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H | @@ -41,10 +41,12 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H | ||
41 | private boolean isDefault; | 41 | private boolean isDefault; |
42 | private DeviceProfileType type; | 42 | private DeviceProfileType type; |
43 | private DeviceTransportType transportType; | 43 | private DeviceTransportType transportType; |
44 | + private DeviceProfileProvisionType provisionType; | ||
44 | private RuleChainId defaultRuleChainId; | 45 | private RuleChainId defaultRuleChainId; |
45 | private transient DeviceProfileData profileData; | 46 | private transient DeviceProfileData profileData; |
46 | @JsonIgnore | 47 | @JsonIgnore |
47 | private byte[] profileDataBytes; | 48 | private byte[] profileDataBytes; |
49 | + private String provisionDeviceKey; | ||
48 | 50 | ||
49 | public DeviceProfile() { | 51 | public DeviceProfile() { |
50 | super(); | 52 | super(); |
@@ -62,6 +64,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H | @@ -62,6 +64,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H | ||
62 | this.isDefault = deviceProfile.isDefault(); | 64 | this.isDefault = deviceProfile.isDefault(); |
63 | this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); | 65 | this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); |
64 | this.setProfileData(deviceProfile.getProfileData()); | 66 | this.setProfileData(deviceProfile.getProfileData()); |
67 | + this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); | ||
65 | } | 68 | } |
66 | 69 | ||
67 | @Override | 70 | @Override |
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileProvisionType.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data; | ||
17 | + | ||
18 | +public enum DeviceProfileProvisionType { | ||
19 | + DISABLED, | ||
20 | + ALLOW_CREATE_NEW_DEVICES, | ||
21 | + CHECK_PRE_PROVISIONED_DEVICES | ||
22 | +} |
@@ -42,7 +42,9 @@ public enum ActionType { | @@ -42,7 +42,9 @@ public enum ActionType { | ||
42 | LOGOUT(false), | 42 | LOGOUT(false), |
43 | LOCKOUT(false), | 43 | LOCKOUT(false), |
44 | ASSIGNED_FROM_TENANT(false), | 44 | ASSIGNED_FROM_TENANT(false), |
45 | - ASSIGNED_TO_TENANT(false); | 45 | + ASSIGNED_TO_TENANT(false), |
46 | + PROVISION_SUCCESS(false), | ||
47 | + PROVISION_FAILURE(false); | ||
46 | 48 | ||
47 | private final boolean isRead; | 49 | private final boolean isRead; |
48 | 50 |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.credentials; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class ProvisionDeviceCredentialsData { | ||
22 | + private final String token; | ||
23 | + private final String clientId; | ||
24 | + private final String username; | ||
25 | + private final String password; | ||
26 | + private final String x509CertHash; | ||
27 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
20 | + | ||
21 | +@Data | ||
22 | +public class AllowCreateNewDevicesDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { | ||
23 | + | ||
24 | + private final String provisionDeviceSecret; | ||
25 | + | ||
26 | + @Override | ||
27 | + public DeviceProfileProvisionType getType() { | ||
28 | + return DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES; | ||
29 | + } | ||
30 | + | ||
31 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
20 | + | ||
21 | +@Data | ||
22 | +public class CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { | ||
23 | + | ||
24 | + private final String provisionDeviceSecret; | ||
25 | + | ||
26 | + @Override | ||
27 | + public DeviceProfileProvisionType getType() { | ||
28 | + return DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES; | ||
29 | + } | ||
30 | + | ||
31 | +} |
@@ -24,6 +24,7 @@ public class DeviceProfileData { | @@ -24,6 +24,7 @@ public class DeviceProfileData { | ||
24 | 24 | ||
25 | private DeviceProfileConfiguration configuration; | 25 | private DeviceProfileConfiguration configuration; |
26 | private DeviceProfileTransportConfiguration transportConfiguration; | 26 | private DeviceProfileTransportConfiguration transportConfiguration; |
27 | + private DeviceProfileProvisionConfiguration provisionConfiguration; | ||
27 | private List<DeviceProfileAlarm> alarms; | 28 | private List<DeviceProfileAlarm> alarms; |
28 | 29 | ||
29 | } | 30 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
19 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
20 | +import com.fasterxml.jackson.annotation.JsonSubTypes; | ||
21 | +import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
22 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
23 | + | ||
24 | + | ||
25 | +@JsonIgnoreProperties(ignoreUnknown = true) | ||
26 | +@JsonTypeInfo( | ||
27 | + use = JsonTypeInfo.Id.NAME, | ||
28 | + include = JsonTypeInfo.As.PROPERTY, | ||
29 | + property = "type") | ||
30 | +@JsonSubTypes({ | ||
31 | + @JsonSubTypes.Type(value = DisabledDeviceProfileProvisionConfiguration.class, name = "DISABLED"), | ||
32 | + @JsonSubTypes.Type(value = AllowCreateNewDevicesDeviceProfileProvisionConfiguration.class, name = "ALLOW_CREATE_NEW_DEVICES"), | ||
33 | + @JsonSubTypes.Type(value = CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.class, name = "CHECK_PRE_PROVISIONED_DEVICES")}) | ||
34 | +public interface DeviceProfileProvisionConfiguration { | ||
35 | + | ||
36 | + String getProvisionDeviceSecret(); | ||
37 | + | ||
38 | + @JsonIgnore | ||
39 | + DeviceProfileProvisionType getType(); | ||
40 | + | ||
41 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
20 | + | ||
21 | +@Data | ||
22 | +public class DisabledDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { | ||
23 | + | ||
24 | + private final String provisionDeviceSecret; | ||
25 | + | ||
26 | + @Override | ||
27 | + public DeviceProfileProvisionType getType() { | ||
28 | + return DeviceProfileProvisionType.DISABLED; | ||
29 | + } | ||
30 | + | ||
31 | +} |
@@ -20,6 +20,8 @@ package org.thingsboard.server.common.data.device.profile; | @@ -20,6 +20,8 @@ package org.thingsboard.server.common.data.device.profile; | ||
20 | */ | 20 | */ |
21 | public class MqttTopics { | 21 | public class MqttTopics { |
22 | 22 | ||
23 | + private static final String REQUEST = "/request"; | ||
24 | + private static final String RESPONSE = "/response"; | ||
23 | private static final String RPC = "/rpc"; | 25 | private static final String RPC = "/rpc"; |
24 | private static final String CONNECT = "/connect"; | 26 | private static final String CONNECT = "/connect"; |
25 | private static final String DISCONNECT = "/disconnect"; | 27 | private static final String DISCONNECT = "/disconnect"; |
@@ -27,11 +29,13 @@ public class MqttTopics { | @@ -27,11 +29,13 @@ public class MqttTopics { | ||
27 | private static final String ATTRIBUTES = "/attributes"; | 29 | private static final String ATTRIBUTES = "/attributes"; |
28 | private static final String CLAIM = "/claim"; | 30 | private static final String CLAIM = "/claim"; |
29 | private static final String SUB_TOPIC = "+"; | 31 | private static final String SUB_TOPIC = "+"; |
30 | - private static final String ATTRIBUTES_RESPONSE = "/attributes/response"; | ||
31 | - private static final String ATTRIBUTES_REQUEST = "/attributes/request"; | 32 | + private static final String PROVISION = "/provision"; |
32 | 33 | ||
33 | - private static final String DEVICE_RPC_RESPONSE = "/rpc/response/"; | ||
34 | - private static final String DEVICE_RPC_REQUEST = "/rpc/request/"; | 34 | + private static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE; |
35 | + private static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; | ||
36 | + | ||
37 | + private static final String DEVICE_RPC_RESPONSE = RPC + RESPONSE + "/"; | ||
38 | + private static final String DEVICE_RPC_REQUEST = RPC + REQUEST + "/"; | ||
35 | 39 | ||
36 | private static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/"; | 40 | private static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/"; |
37 | private static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; | 41 | private static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; |
@@ -50,6 +54,8 @@ public class MqttTopics { | @@ -50,6 +54,8 @@ public class MqttTopics { | ||
50 | public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRY; | 54 | public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRY; |
51 | public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + CLAIM; | 55 | public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + CLAIM; |
52 | public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES; | 56 | public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES; |
57 | + public static final String DEVICE_PROVISION_REQUEST_TOPIC = PROVISION + REQUEST; | ||
58 | + public static final String DEVICE_PROVISION_RESPONSE_TOPIC = PROVISION + RESPONSE; | ||
53 | 59 | ||
54 | // V1_JSON gateway topics | 60 | // V1_JSON gateway topics |
55 | 61 |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class ProvisionDeviceProfileCredentials { | ||
22 | + private final String provisionDeviceKey; | ||
23 | + private final String provisionDeviceSecret; | ||
24 | +} |
@@ -16,5 +16,5 @@ | @@ -16,5 +16,5 @@ | ||
16 | package org.thingsboard.server.common.msg.session; | 16 | package org.thingsboard.server.common.msg.session; |
17 | 17 | ||
18 | public enum FeatureType { | 18 | public enum FeatureType { |
19 | - ATTRIBUTES, TELEMETRY, RPC, CLAIM | 19 | + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION |
20 | } | 20 | } |
@@ -73,6 +73,12 @@ enum KeyValueType { | @@ -73,6 +73,12 @@ enum KeyValueType { | ||
73 | JSON_V = 4; | 73 | JSON_V = 4; |
74 | } | 74 | } |
75 | 75 | ||
76 | +enum CredentialsType { | ||
77 | + ACCESS_TOKEN = 0; | ||
78 | + X509_CERTIFICATE = 1; | ||
79 | + MQTT_BASIC = 2; | ||
80 | +} | ||
81 | + | ||
76 | message KeyValueProto { | 82 | message KeyValueProto { |
77 | string key = 1; | 83 | string key = 1; |
78 | KeyValueType type = 2; | 84 | KeyValueType type = 2; |
@@ -241,6 +247,43 @@ message ClaimDeviceMsg { | @@ -241,6 +247,43 @@ message ClaimDeviceMsg { | ||
241 | int64 durationMs = 4; | 247 | int64 durationMs = 4; |
242 | } | 248 | } |
243 | 249 | ||
250 | +message DeviceCredentialsProto { | ||
251 | + int64 deviceIdMSB = 1; | ||
252 | + int64 deviceIdLSB = 2; | ||
253 | + CredentialsType credentialsType = 3; | ||
254 | + string credentialsId = 4; | ||
255 | + string credentialsValue = 5; | ||
256 | +} | ||
257 | + | ||
258 | +message CredentialsDataProto { | ||
259 | + ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1; | ||
260 | + ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2; | ||
261 | + ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3; | ||
262 | +} | ||
263 | + | ||
264 | +message ProvisionDeviceRequestMsg { | ||
265 | + string deviceName = 1; | ||
266 | + CredentialsType credentialsType = 2; | ||
267 | + ProvisionDeviceCredentialsMsg provisionDeviceCredentialsMsg = 3; | ||
268 | + CredentialsDataProto credentialsDataProto = 4; | ||
269 | +} | ||
270 | + | ||
271 | +message ProvisionDeviceCredentialsMsg { | ||
272 | + string provisionDeviceKey = 1; | ||
273 | + string provisionDeviceSecret = 2; | ||
274 | +} | ||
275 | + | ||
276 | +message ProvisionDeviceResponseMsg { | ||
277 | + DeviceCredentialsProto deviceCredentials = 1; | ||
278 | + ProvisionResponseStatus provisionResponseStatus = 2; | ||
279 | +} | ||
280 | + | ||
281 | +enum ProvisionResponseStatus { | ||
282 | + UNKNOWN = 0; | ||
283 | + SUCCESS = 1; | ||
284 | + NOT_FOUND = 2; | ||
285 | + FAILURE = 3; | ||
286 | +} | ||
244 | //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. | 287 | //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. |
245 | message SubscriptionInfoProto { | 288 | message SubscriptionInfoProto { |
246 | int64 lastActivityTime = 1; | 289 | int64 lastActivityTime = 1; |
@@ -266,6 +309,7 @@ message TransportToDeviceActorMsg { | @@ -266,6 +309,7 @@ message TransportToDeviceActorMsg { | ||
266 | ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 6; | 309 | ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 6; |
267 | SubscriptionInfoProto subscriptionInfo = 7; | 310 | SubscriptionInfoProto subscriptionInfo = 7; |
268 | ClaimDeviceMsg claimDevice = 8; | 311 | ClaimDeviceMsg claimDevice = 8; |
312 | + ProvisionDeviceRequestMsg provisionDevice = 9; | ||
269 | } | 313 | } |
270 | 314 | ||
271 | message TransportToRuleEngineMsg { | 315 | message TransportToRuleEngineMsg { |
@@ -441,6 +485,7 @@ message TransportApiRequestMsg { | @@ -441,6 +485,7 @@ message TransportApiRequestMsg { | ||
441 | GetTenantRoutingInfoRequestMsg getTenantRoutingInfoRequestMsg = 4; | 485 | GetTenantRoutingInfoRequestMsg getTenantRoutingInfoRequestMsg = 4; |
442 | GetDeviceProfileRequestMsg getDeviceProfileRequestMsg = 5; | 486 | GetDeviceProfileRequestMsg getDeviceProfileRequestMsg = 5; |
443 | ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6; | 487 | ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6; |
488 | + ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; | ||
444 | } | 489 | } |
445 | 490 | ||
446 | /* Response from ThingsBoard Core Service to Transport Service */ | 491 | /* Response from ThingsBoard Core Service to Transport Service */ |
@@ -449,6 +494,7 @@ message TransportApiResponseMsg { | @@ -449,6 +494,7 @@ message TransportApiResponseMsg { | ||
449 | GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; | 494 | GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; |
450 | GetTenantRoutingInfoResponseMsg getTenantRoutingInfoResponseMsg = 4; | 495 | GetTenantRoutingInfoResponseMsg getTenantRoutingInfoResponseMsg = 4; |
451 | GetDeviceProfileResponseMsg getDeviceProfileResponseMsg = 5; | 496 | GetDeviceProfileResponseMsg getDeviceProfileResponseMsg = 5; |
497 | + ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 6; | ||
452 | } | 498 | } |
453 | 499 | ||
454 | /* Messages that are handled by ThingsBoard Core Service */ | 500 | /* Messages that are handled by ThingsBoard Core Service */ |
@@ -491,4 +537,5 @@ message ToTransportMsg { | @@ -491,4 +537,5 @@ message ToTransportMsg { | ||
491 | ToServerRpcResponseMsg toServerResponse = 7; | 537 | ToServerRpcResponseMsg toServerResponse = 7; |
492 | DeviceProfileUpdateMsg deviceProfileUpdateMsg = 8; | 538 | DeviceProfileUpdateMsg deviceProfileUpdateMsg = 8; |
493 | DeviceProfileDeleteMsg deviceProfileDeleteMsg = 9; | 539 | DeviceProfileDeleteMsg deviceProfileDeleteMsg = 9; |
540 | + ProvisionDeviceResponseMsg provisionResponse = 10; | ||
494 | } | 541 | } |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
@@ -24,6 +24,7 @@ import org.eclipse.californium.core.network.ExchangeObserver; | @@ -24,6 +24,7 @@ import org.eclipse.californium.core.network.ExchangeObserver; | ||
24 | import org.eclipse.californium.core.server.resources.CoapExchange; | 24 | import org.eclipse.californium.core.server.resources.CoapExchange; |
25 | import org.eclipse.californium.core.server.resources.Resource; | 25 | import org.eclipse.californium.core.server.resources.Resource; |
26 | import org.springframework.util.ReflectionUtils; | 26 | import org.springframework.util.ReflectionUtils; |
27 | +import org.thingsboard.server.common.data.DataConstants; | ||
27 | import org.thingsboard.server.common.data.DeviceTransportType; | 28 | import org.thingsboard.server.common.data.DeviceTransportType; |
28 | import org.thingsboard.server.common.data.security.DeviceTokenCredentials; | 29 | import org.thingsboard.server.common.data.security.DeviceTokenCredentials; |
29 | import org.thingsboard.server.common.msg.session.FeatureType; | 30 | import org.thingsboard.server.common.msg.session.FeatureType; |
@@ -33,9 +34,11 @@ import org.thingsboard.server.common.transport.TransportContext; | @@ -33,9 +34,11 @@ import org.thingsboard.server.common.transport.TransportContext; | ||
33 | import org.thingsboard.server.common.transport.TransportService; | 34 | import org.thingsboard.server.common.transport.TransportService; |
34 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 35 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
35 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 36 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
37 | +import org.thingsboard.server.common.transport.adaptor.JsonConverter; | ||
36 | import org.thingsboard.server.common.transport.auth.SessionInfoCreator; | 38 | import org.thingsboard.server.common.transport.auth.SessionInfoCreator; |
37 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | 39 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
38 | import org.thingsboard.server.gen.transport.TransportProtos; | 40 | import org.thingsboard.server.gen.transport.TransportProtos; |
41 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
39 | 42 | ||
40 | import java.lang.reflect.Field; | 43 | import java.lang.reflect.Field; |
41 | import java.util.List; | 44 | import java.util.List; |
@@ -130,10 +133,25 @@ public class CoapTransportResource extends CoapResource { | @@ -130,10 +133,25 @@ public class CoapTransportResource extends CoapResource { | ||
130 | case CLAIM: | 133 | case CLAIM: |
131 | processRequest(exchange, SessionMsgType.CLAIM_REQUEST); | 134 | processRequest(exchange, SessionMsgType.CLAIM_REQUEST); |
132 | break; | 135 | break; |
136 | + case PROVISION: | ||
137 | + processProvision(exchange); | ||
138 | + break; | ||
133 | } | 139 | } |
134 | } | 140 | } |
135 | } | 141 | } |
136 | 142 | ||
143 | + private void processProvision(CoapExchange exchange) { | ||
144 | + log.trace("Processing {}", exchange.advanced().getRequest()); | ||
145 | + exchange.accept(); | ||
146 | + try { | ||
147 | + transportService.process(transportContext.getAdaptor().convertToProvisionRequestMsg(UUID.randomUUID(), exchange.advanced().getRequest()), | ||
148 | + new DeviceProvisionCallback(exchange)); | ||
149 | + } catch (AdaptorException e) { | ||
150 | + log.trace("Failed to decode message: ", e); | ||
151 | + exchange.respond(ResponseCode.BAD_REQUEST); | ||
152 | + } | ||
153 | + } | ||
154 | + | ||
137 | private void processRequest(CoapExchange exchange, SessionMsgType type) { | 155 | private void processRequest(CoapExchange exchange, SessionMsgType type) { |
138 | log.trace("Processing {}", exchange.advanced().getRequest()); | 156 | log.trace("Processing {}", exchange.advanced().getRequest()); |
139 | exchange.accept(); | 157 | exchange.accept(); |
@@ -274,6 +292,8 @@ public class CoapTransportResource extends CoapResource { | @@ -274,6 +292,8 @@ public class CoapTransportResource extends CoapResource { | ||
274 | try { | 292 | try { |
275 | if (uriPath.size() >= FEATURE_TYPE_POSITION) { | 293 | if (uriPath.size() >= FEATURE_TYPE_POSITION) { |
276 | return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase())); | 294 | return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase())); |
295 | + } else if (uriPath.size() == 3 && uriPath.contains(DataConstants.PROVISION)) { | ||
296 | + return Optional.of(FeatureType.valueOf(DataConstants.PROVISION.toUpperCase())); | ||
277 | } | 297 | } |
278 | } catch (RuntimeException e) { | 298 | } catch (RuntimeException e) { |
279 | log.warn("Failed to decode feature type: {}", uriPath); | 299 | log.warn("Failed to decode feature type: {}", uriPath); |
@@ -325,6 +345,25 @@ public class CoapTransportResource extends CoapResource { | @@ -325,6 +345,25 @@ public class CoapTransportResource extends CoapResource { | ||
325 | } | 345 | } |
326 | } | 346 | } |
327 | 347 | ||
348 | + private static class DeviceProvisionCallback implements TransportServiceCallback<ProvisionDeviceResponseMsg> { | ||
349 | + private final CoapExchange exchange; | ||
350 | + | ||
351 | + DeviceProvisionCallback(CoapExchange exchange) { | ||
352 | + this.exchange = exchange; | ||
353 | + } | ||
354 | + | ||
355 | + @Override | ||
356 | + public void onSuccess(TransportProtos.ProvisionDeviceResponseMsg msg) { | ||
357 | + exchange.respond(JsonConverter.toJson(msg).toString()); | ||
358 | + } | ||
359 | + | ||
360 | + @Override | ||
361 | + public void onError(Throwable e) { | ||
362 | + log.warn("Failed to process request", e); | ||
363 | + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); | ||
364 | + } | ||
365 | + } | ||
366 | + | ||
328 | private static class CoapOkCallback implements TransportServiceCallback<Void> { | 367 | private static class CoapOkCallback implements TransportServiceCallback<Void> { |
329 | private final CoapExchange exchange; | 368 | private final CoapExchange exchange; |
330 | 369 |
@@ -19,6 +19,7 @@ import org.eclipse.californium.core.coap.Request; | @@ -19,6 +19,7 @@ import org.eclipse.californium.core.coap.Request; | ||
19 | import org.eclipse.californium.core.coap.Response; | 19 | import org.eclipse.californium.core.coap.Response; |
20 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 20 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
21 | import org.thingsboard.server.gen.transport.TransportProtos; | 21 | import org.thingsboard.server.gen.transport.TransportProtos; |
22 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; | ||
22 | import org.thingsboard.server.transport.coap.CoapTransportResource; | 23 | import org.thingsboard.server.transport.coap.CoapTransportResource; |
23 | 24 | ||
24 | import java.util.UUID; | 25 | import java.util.UUID; |
@@ -45,4 +46,6 @@ public interface CoapTransportAdaptor { | @@ -45,4 +46,6 @@ public interface CoapTransportAdaptor { | ||
45 | 46 | ||
46 | Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException; | 47 | Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException; |
47 | 48 | ||
49 | + ProvisionDeviceRequestMsg convertToProvisionRequestMsg(UUID sessionId, Request inbound) throws AdaptorException; | ||
50 | + | ||
48 | } | 51 | } |
@@ -124,6 +124,16 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | @@ -124,6 +124,16 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | ||
124 | } | 124 | } |
125 | 125 | ||
126 | @Override | 126 | @Override |
127 | + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(UUID sessionId, Request inbound) throws AdaptorException { | ||
128 | + String payload = validatePayload(sessionId, inbound, false); | ||
129 | + try { | ||
130 | + return JsonConverter.convertToProvisionRequestMsg(payload); | ||
131 | + } catch (IllegalStateException | JsonSyntaxException ex) { | ||
132 | + throw new AdaptorException(ex); | ||
133 | + } | ||
134 | + } | ||
135 | + | ||
136 | + @Override | ||
127 | public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { | 137 | public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { |
128 | if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) { | 138 | if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) { |
129 | return new Response(CoAP.ResponseCode.NOT_FOUND); | 139 | return new Response(CoAP.ResponseCode.NOT_FOUND); |
@@ -44,6 +44,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotif | @@ -44,6 +44,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotif | ||
44 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 44 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
45 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | 45 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; |
46 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; | 46 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
47 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
47 | import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; |
48 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 49 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
49 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | 50 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; |
@@ -203,6 +204,14 @@ public class DeviceApiController { | @@ -203,6 +204,14 @@ public class DeviceApiController { | ||
203 | return responseWriter; | 204 | return responseWriter; |
204 | } | 205 | } |
205 | 206 | ||
207 | + @RequestMapping(value = "/provision", method = RequestMethod.POST) | ||
208 | + public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) { | ||
209 | + DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); | ||
210 | + transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), | ||
211 | + new DeviceProvisionCallback(responseWriter)); | ||
212 | + return responseWriter; | ||
213 | + } | ||
214 | + | ||
206 | private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> { | 215 | private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> { |
207 | private final TransportContext transportContext; | 216 | private final TransportContext transportContext; |
208 | private final DeferredResult<ResponseEntity> responseWriter; | 217 | private final DeferredResult<ResponseEntity> responseWriter; |
@@ -230,6 +239,25 @@ public class DeviceApiController { | @@ -230,6 +239,25 @@ public class DeviceApiController { | ||
230 | } | 239 | } |
231 | } | 240 | } |
232 | 241 | ||
242 | + private static class DeviceProvisionCallback implements TransportServiceCallback<ProvisionDeviceResponseMsg> { | ||
243 | + private final DeferredResult<ResponseEntity> responseWriter; | ||
244 | + | ||
245 | + DeviceProvisionCallback(DeferredResult<ResponseEntity> responseWriter) { | ||
246 | + this.responseWriter = responseWriter; | ||
247 | + } | ||
248 | + | ||
249 | + @Override | ||
250 | + public void onSuccess(ProvisionDeviceResponseMsg msg) { | ||
251 | + responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); | ||
252 | + } | ||
253 | + | ||
254 | + @Override | ||
255 | + public void onError(Throwable e) { | ||
256 | + log.warn("Failed to process request", e); | ||
257 | + responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); | ||
258 | + } | ||
259 | + } | ||
260 | + | ||
233 | private static class SessionCloseOnErrorCallback implements TransportServiceCallback<Void> { | 261 | private static class SessionCloseOnErrorCallback implements TransportServiceCallback<Void> { |
234 | private final TransportService transportService; | 262 | private final TransportService transportService; |
235 | private final SessionInfoProto sessionInfo; | 263 | private final SessionInfoProto sessionInfo; |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.server.transport.mqtt; | 16 | package org.thingsboard.server.transport.mqtt; |
17 | 17 | ||
18 | import com.fasterxml.jackson.databind.JsonNode; | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | +import com.google.gson.JsonParseException; | ||
19 | import io.netty.channel.ChannelHandlerContext; | 20 | import io.netty.channel.ChannelHandlerContext; |
20 | import io.netty.channel.ChannelInboundHandlerAdapter; | 21 | import io.netty.channel.ChannelInboundHandlerAdapter; |
21 | import io.netty.handler.codec.mqtt.MqttConnAckMessage; | 22 | import io.netty.handler.codec.mqtt.MqttConnAckMessage; |
@@ -38,8 +39,10 @@ import io.netty.util.ReferenceCountUtil; | @@ -38,8 +39,10 @@ import io.netty.util.ReferenceCountUtil; | ||
38 | import io.netty.util.concurrent.Future; | 39 | import io.netty.util.concurrent.Future; |
39 | import io.netty.util.concurrent.GenericFutureListener; | 40 | import io.netty.util.concurrent.GenericFutureListener; |
40 | import lombok.extern.slf4j.Slf4j; | 41 | import lombok.extern.slf4j.Slf4j; |
42 | +import org.thingsboard.server.common.data.DataConstants; | ||
41 | import org.thingsboard.server.common.data.DeviceProfile; | 43 | import org.thingsboard.server.common.data.DeviceProfile; |
42 | import org.thingsboard.server.common.data.DeviceTransportType; | 44 | import org.thingsboard.server.common.data.DeviceTransportType; |
45 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
43 | import org.thingsboard.server.common.data.device.profile.MqttTopics; | 46 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
44 | import org.thingsboard.server.common.msg.EncryptionUtil; | 47 | import org.thingsboard.server.common.msg.EncryptionUtil; |
45 | import org.thingsboard.server.common.transport.SessionMsgListener; | 48 | import org.thingsboard.server.common.transport.SessionMsgListener; |
@@ -51,6 +54,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | @@ -51,6 +54,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | ||
51 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | 54 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
52 | import org.thingsboard.server.common.transport.service.DefaultTransportService; | 55 | import org.thingsboard.server.common.transport.service.DefaultTransportService; |
53 | import org.thingsboard.server.gen.transport.TransportProtos; | 56 | import org.thingsboard.server.gen.transport.TransportProtos; |
57 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
54 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; | 58 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; |
55 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 59 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
56 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; | 60 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; |
@@ -68,11 +72,12 @@ import java.util.List; | @@ -68,11 +72,12 @@ import java.util.List; | ||
68 | import java.util.UUID; | 72 | import java.util.UUID; |
69 | import java.util.concurrent.ConcurrentHashMap; | 73 | import java.util.concurrent.ConcurrentHashMap; |
70 | import java.util.concurrent.ConcurrentMap; | 74 | import java.util.concurrent.ConcurrentMap; |
71 | -import java.util.Date; | 75 | +import java.util.concurrent.TimeUnit; |
72 | 76 | ||
73 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; | 77 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; |
74 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; | 78 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; |
75 | import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; | 79 | import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; |
80 | +import static io.netty.handler.codec.mqtt.MqttMessageType.CONNECT; | ||
76 | import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; | 81 | import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; |
77 | import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; | 82 | import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; |
78 | import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; | 83 | import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; |
@@ -130,10 +135,58 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -130,10 +135,58 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
130 | return; | 135 | return; |
131 | } | 136 | } |
132 | deviceSessionCtx.setChannel(ctx); | 137 | deviceSessionCtx.setChannel(ctx); |
138 | + if (CONNECT.equals(msg.fixedHeader().messageType())) { | ||
139 | + processConnect(ctx, (MqttConnectMessage) msg); | ||
140 | + } else if (deviceSessionCtx.isProvisionOnly()) { | ||
141 | + processProvisionSessionMsg(ctx, msg); | ||
142 | + } else { | ||
143 | + processRegularSessionMsg(ctx, msg); | ||
144 | + } | ||
145 | + } | ||
146 | + | ||
147 | + private void processProvisionSessionMsg(ChannelHandlerContext ctx, MqttMessage msg) { | ||
133 | switch (msg.fixedHeader().messageType()) { | 148 | switch (msg.fixedHeader().messageType()) { |
134 | - case CONNECT: | ||
135 | - processConnect(ctx, (MqttConnectMessage) msg); | 149 | + case PUBLISH: |
150 | + MqttPublishMessage mqttMsg = (MqttPublishMessage) msg; | ||
151 | + String topicName = mqttMsg.variableHeader().topicName(); | ||
152 | + int msgId = mqttMsg.variableHeader().packetId(); | ||
153 | + try { | ||
154 | + if (topicName.equals(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC)) { | ||
155 | + try { | ||
156 | + TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = deviceSessionCtx.getContext().getJsonMqttAdaptor().convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg); | ||
157 | + transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg)); | ||
158 | + log.trace("[{}][{}] Processing provision publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId); | ||
159 | + } catch (Exception e) { | ||
160 | + if (e instanceof JsonParseException || (e.getCause() != null && e.getCause() instanceof JsonParseException)) { | ||
161 | + TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = deviceSessionCtx.getContext().getProtoMqttAdaptor().convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg); | ||
162 | + transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg)); | ||
163 | + deviceSessionCtx.setProvisionPayloadType(TransportPayloadType.PROTOBUF); | ||
164 | + log.trace("[{}][{}] Processing provision publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId); | ||
165 | + } else { | ||
166 | + throw e; | ||
167 | + } | ||
168 | + } | ||
169 | + } else { | ||
170 | + throw new RuntimeException("Unsupported topic for provisioning requests!"); | ||
171 | + } | ||
172 | + } catch (RuntimeException | AdaptorException e) { | ||
173 | + log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); | ||
174 | + ctx.close(); | ||
175 | + } | ||
136 | break; | 176 | break; |
177 | + case PINGREQ: | ||
178 | + ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); | ||
179 | + break; | ||
180 | + case DISCONNECT: | ||
181 | + if (checkConnected(ctx, msg)) { | ||
182 | + processDisconnect(ctx); | ||
183 | + } | ||
184 | + break; | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + private void processRegularSessionMsg(ChannelHandlerContext ctx, MqttMessage msg) { | ||
189 | + switch (msg.fixedHeader().messageType()) { | ||
137 | case PUBLISH: | 190 | case PUBLISH: |
138 | processPublish(ctx, (MqttPublishMessage) msg); | 191 | processPublish(ctx, (MqttPublishMessage) msg); |
139 | break; | 192 | break; |
@@ -257,6 +310,42 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -257,6 +310,42 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
257 | }; | 310 | }; |
258 | } | 311 | } |
259 | 312 | ||
313 | + private class DeviceProvisionCallback implements TransportServiceCallback<ProvisionDeviceResponseMsg> { | ||
314 | + private final ChannelHandlerContext ctx; | ||
315 | + private final int msgId; | ||
316 | + private final TransportProtos.ProvisionDeviceRequestMsg msg; | ||
317 | + | ||
318 | + DeviceProvisionCallback(ChannelHandlerContext ctx, int msgId, TransportProtos.ProvisionDeviceRequestMsg msg) { | ||
319 | + this.ctx = ctx; | ||
320 | + this.msgId = msgId; | ||
321 | + this.msg = msg; | ||
322 | + } | ||
323 | + | ||
324 | + @Override | ||
325 | + public void onSuccess(TransportProtos.ProvisionDeviceResponseMsg provisionResponseMsg) { | ||
326 | + log.trace("[{}] Published msg: {}", sessionId, msg); | ||
327 | + if (msgId > 0) { | ||
328 | + ctx.writeAndFlush(createMqttPubAckMsg(msgId)); | ||
329 | + } | ||
330 | + try { | ||
331 | + if (deviceSessionCtx.getProvisionPayloadType().equals(TransportPayloadType.JSON)) { | ||
332 | + deviceSessionCtx.getContext().getJsonMqttAdaptor().convertToPublish(deviceSessionCtx, provisionResponseMsg).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); | ||
333 | + } else { | ||
334 | + deviceSessionCtx.getContext().getProtoMqttAdaptor().convertToPublish(deviceSessionCtx, provisionResponseMsg).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); | ||
335 | + } | ||
336 | + transportService.getSchedulerExecutor().schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS); | ||
337 | + } catch (Exception e) { | ||
338 | + log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | ||
339 | + } | ||
340 | + } | ||
341 | + | ||
342 | + @Override | ||
343 | + public void onError(Throwable e) { | ||
344 | + log.trace("[{}] Failed to publish msg: {}", sessionId, msg, e); | ||
345 | + processDisconnect(ctx); | ||
346 | + } | ||
347 | + } | ||
348 | + | ||
260 | private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { | 349 | private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { |
261 | if (!checkConnected(ctx, mqttMsg)) { | 350 | if (!checkConnected(ctx, mqttMsg)) { |
262 | return; | 351 | return; |
@@ -286,6 +375,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -286,6 +375,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
286 | case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: | 375 | case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: |
287 | case MqttTopics.GATEWAY_RPC_TOPIC: | 376 | case MqttTopics.GATEWAY_RPC_TOPIC: |
288 | case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: | 377 | case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: |
378 | + case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: | ||
289 | registerSubQoS(topic, grantedQoSList, reqQoS); | 379 | registerSubQoS(topic, grantedQoSList, reqQoS); |
290 | break; | 380 | break; |
291 | default: | 381 | default: |
@@ -351,11 +441,18 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -351,11 +441,18 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
351 | 441 | ||
352 | private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { | 442 | private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { |
353 | log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier()); | 443 | log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier()); |
354 | - X509Certificate cert; | ||
355 | - if (sslHandler != null && (cert = getX509Certificate()) != null) { | ||
356 | - processX509CertConnect(ctx, cert); | 444 | + String userName = msg.payload().userName(); |
445 | + String clientId = msg.payload().clientIdentifier(); | ||
446 | + if (DataConstants.PROVISION.equals(userName) || DataConstants.PROVISION.equals(clientId)) { | ||
447 | + deviceSessionCtx.setProvisionOnly(true); | ||
448 | + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); | ||
357 | } else { | 449 | } else { |
358 | - processAuthTokenConnect(ctx, msg); | 450 | + X509Certificate cert; |
451 | + if (sslHandler != null && (cert = getX509Certificate()) != null) { | ||
452 | + processX509CertConnect(ctx, cert); | ||
453 | + } else { | ||
454 | + processAuthTokenConnect(ctx, msg); | ||
455 | + } | ||
359 | } | 456 | } |
360 | } | 457 | } |
361 | 458 | ||
@@ -387,7 +484,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -387,7 +484,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
387 | 484 | ||
388 | private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) { | 485 | private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) { |
389 | try { | 486 | try { |
390 | - if(!context.isSkipValidityCheckForClientCert()){ | 487 | + if (!context.isSkipValidityCheckForClientCert()) { |
391 | cert.checkValidity(); | 488 | cert.checkValidity(); |
392 | } | 489 | } |
393 | String strCert = SslUtil.getX509CertificateString(cert); | 490 | String strCert = SslUtil.getX509CertificateString(cert); |
@@ -88,6 +88,16 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -88,6 +88,16 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
88 | } | 88 | } |
89 | 89 | ||
90 | @Override | 90 | @Override |
91 | + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
92 | + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); | ||
93 | + try { | ||
94 | + return JsonConverter.convertToProvisionRequestMsg(payload); | ||
95 | + } catch (IllegalStateException | JsonSyntaxException ex) { | ||
96 | + throw new AdaptorException(ex); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + @Override | ||
91 | public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 101 | public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
92 | return processGetAttributeRequestMsg(inbound, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX); | 102 | return processGetAttributeRequestMsg(inbound, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX); |
93 | } | 103 | } |
@@ -138,6 +148,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -138,6 +148,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
138 | return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); | 148 | return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); |
139 | } | 149 | } |
140 | 150 | ||
151 | + @Override | ||
152 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ProvisionDeviceResponseMsg provisionResponse) { | ||
153 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, JsonConverter.toJson(provisionResponse))); | ||
154 | + } | ||
155 | + | ||
141 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | 156 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { |
142 | String payload = validatePayload(sessionId, payloadData, false); | 157 | String payload = validatePayload(sessionId, payloadData, false); |
143 | try { | 158 | try { |
@@ -24,6 +24,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestM | @@ -24,6 +24,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestM | ||
24 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; | 24 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
25 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | 25 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; |
26 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | 26 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; |
27 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; | ||
28 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
27 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; | 29 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; |
28 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; | 30 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; |
29 | import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; | 31 | import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; |
@@ -63,4 +65,8 @@ public interface MqttTransportAdaptor { | @@ -63,4 +65,8 @@ public interface MqttTransportAdaptor { | ||
63 | 65 | ||
64 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException; | 66 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException; |
65 | 67 | ||
68 | + ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; | ||
69 | + | ||
70 | + Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; | ||
71 | + | ||
66 | } | 72 | } |
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; | @@ -32,6 +32,7 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; | ||
32 | import org.thingsboard.server.common.transport.adaptor.ProtoConverter; | 32 | import org.thingsboard.server.common.transport.adaptor.ProtoConverter; |
33 | import org.thingsboard.server.gen.transport.TransportApiProtos; | 33 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
34 | import org.thingsboard.server.gen.transport.TransportProtos; | 34 | import org.thingsboard.server.gen.transport.TransportProtos; |
35 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
35 | import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; | 36 | import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; |
36 | 37 | ||
37 | import java.util.Optional; | 38 | import java.util.Optional; |
@@ -109,6 +110,17 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { | @@ -109,6 +110,17 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { | ||
109 | } | 110 | } |
110 | 111 | ||
111 | @Override | 112 | @Override |
113 | + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException { | ||
114 | + byte[] bytes = toBytes(mqttMsg.payload()); | ||
115 | + String topicName = mqttMsg.variableHeader().topicName(); | ||
116 | + try { | ||
117 | + return ProtoConverter.convertToProvisionRequestMsg(bytes); | ||
118 | + } catch (InvalidProtocolBufferException ex) { | ||
119 | + throw new AdaptorException(ex); | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + @Override | ||
112 | public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 124 | public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
113 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 125 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
114 | throw new AdaptorException(responseMsg.getError()); | 126 | throw new AdaptorException(responseMsg.getError()); |
@@ -140,6 +152,11 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { | @@ -140,6 +152,11 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { | ||
140 | } | 152 | } |
141 | 153 | ||
142 | @Override | 154 | @Override |
155 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ProvisionDeviceResponseMsg provisionResponse) { | ||
156 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, provisionResponse.toByteArray())); | ||
157 | + } | ||
158 | + | ||
159 | + @Override | ||
143 | public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 160 | public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
144 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 161 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
145 | throw new AdaptorException(responseMsg.getError()); | 162 | throw new AdaptorException(responseMsg.getError()); |
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt.session; | @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt.session; | ||
17 | 17 | ||
18 | import io.netty.channel.ChannelHandlerContext; | 18 | import io.netty.channel.ChannelHandlerContext; |
19 | import lombok.Getter; | 19 | import lombok.Getter; |
20 | +import lombok.Setter; | ||
20 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
21 | import org.thingsboard.server.common.data.DeviceProfile; | 22 | import org.thingsboard.server.common.data.DeviceProfile; |
22 | import org.thingsboard.server.common.data.DeviceTransportType; | 23 | import org.thingsboard.server.common.data.DeviceTransportType; |
@@ -46,10 +47,18 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | @@ -46,10 +47,18 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | ||
46 | 47 | ||
47 | private final AtomicInteger msgIdSeq = new AtomicInteger(0); | 48 | private final AtomicInteger msgIdSeq = new AtomicInteger(0); |
48 | 49 | ||
50 | + @Getter | ||
51 | + @Setter | ||
52 | + private boolean provisionOnly = false; | ||
53 | + | ||
49 | private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); | 54 | private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); |
50 | private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); | 55 | private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); |
51 | private volatile TransportPayloadType payloadType = TransportPayloadType.JSON; | 56 | private volatile TransportPayloadType payloadType = TransportPayloadType.JSON; |
52 | 57 | ||
58 | + @Getter | ||
59 | + @Setter | ||
60 | + private TransportPayloadType provisionPayloadType = payloadType; | ||
61 | + | ||
53 | public DeviceSessionCtx(UUID sessionId, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap, MqttTransportContext context) { | 62 | public DeviceSessionCtx(UUID sessionId, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap, MqttTransportContext context) { |
54 | super(sessionId, mqttQoSMap); | 63 | super(sessionId, mqttQoSMap); |
55 | this.context = context; | 64 | this.context = context; |
@@ -27,6 +27,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfo | @@ -27,6 +27,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfo | ||
27 | import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoResponseMsg; | 27 | import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoResponseMsg; |
28 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | 28 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; |
29 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | 29 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; |
30 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; | ||
31 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
30 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; | 32 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; |
31 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 33 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
32 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | 34 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; |
@@ -38,6 +40,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCre | @@ -38,6 +40,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCre | ||
38 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | 40 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; |
39 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 41 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
40 | 42 | ||
43 | +import java.util.concurrent.ScheduledExecutorService; | ||
44 | + | ||
41 | /** | 45 | /** |
42 | * Created by ashvayka on 04.10.18. | 46 | * Created by ashvayka on 04.10.18. |
43 | */ | 47 | */ |
@@ -57,6 +61,9 @@ public interface TransportService { | @@ -57,6 +61,9 @@ public interface TransportService { | ||
57 | void process(GetOrCreateDeviceFromGatewayRequestMsg msg, | 61 | void process(GetOrCreateDeviceFromGatewayRequestMsg msg, |
58 | TransportServiceCallback<GetOrCreateDeviceFromGatewayResponse> callback); | 62 | TransportServiceCallback<GetOrCreateDeviceFromGatewayResponse> callback); |
59 | 63 | ||
64 | + void process(ProvisionDeviceRequestMsg msg, | ||
65 | + TransportServiceCallback<ProvisionDeviceResponseMsg> callback); | ||
66 | + | ||
60 | void getDeviceProfile(DeviceProfileId deviceProfileId, TransportServiceCallback<DeviceProfile> callback); | 67 | void getDeviceProfile(DeviceProfileId deviceProfileId, TransportServiceCallback<DeviceProfile> callback); |
61 | 68 | ||
62 | void onProfileUpdate(DeviceProfile deviceProfile); | 69 | void onProfileUpdate(DeviceProfile deviceProfile); |
@@ -83,6 +90,8 @@ public interface TransportService { | @@ -83,6 +90,8 @@ public interface TransportService { | ||
83 | 90 | ||
84 | void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback); | 91 | void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback); |
85 | 92 | ||
93 | + ScheduledExecutorService getSchedulerExecutor(); | ||
94 | + | ||
86 | void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); | 95 | void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); |
87 | 96 | ||
88 | void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); | 97 | void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); |
@@ -90,5 +99,4 @@ public interface TransportService { | @@ -90,5 +99,4 @@ public interface TransportService { | ||
90 | void reportActivity(SessionInfoProto sessionInfo); | 99 | void reportActivity(SessionInfoProto sessionInfo); |
91 | 100 | ||
92 | void deregisterSession(SessionInfoProto sessionInfo); | 101 | void deregisterSession(SessionInfoProto sessionInfo); |
93 | - | ||
94 | } | 102 | } |
@@ -37,13 +37,19 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | @@ -37,13 +37,19 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
37 | import org.thingsboard.server.gen.transport.TransportProtos; | 37 | import org.thingsboard.server.gen.transport.TransportProtos; |
38 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; | 38 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
39 | import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; | 39 | import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; |
40 | +import org.thingsboard.server.gen.transport.TransportProtos.CredentialsType; | ||
40 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; | 41 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
41 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; | 42 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; |
42 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; | 43 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; |
43 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | 44 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; |
44 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | 45 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; |
46 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
47 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus; | ||
45 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto; |
46 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; | 49 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; |
50 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; | ||
51 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | ||
52 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | ||
47 | 53 | ||
48 | import java.util.ArrayList; | 54 | import java.util.ArrayList; |
49 | import java.util.HashMap; | 55 | import java.util.HashMap; |
@@ -53,6 +59,7 @@ import java.util.Map; | @@ -53,6 +59,7 @@ import java.util.Map; | ||
53 | import java.util.Map.Entry; | 59 | import java.util.Map.Entry; |
54 | import java.util.Set; | 60 | import java.util.Set; |
55 | import java.util.TreeMap; | 61 | import java.util.TreeMap; |
62 | +import java.util.UUID; | ||
56 | import java.util.function.Consumer; | 63 | import java.util.function.Consumer; |
57 | import java.util.stream.Collectors; | 64 | import java.util.stream.Collectors; |
58 | 65 | ||
@@ -397,6 +404,36 @@ public class JsonConverter { | @@ -397,6 +404,36 @@ public class JsonConverter { | ||
397 | } | 404 | } |
398 | } | 405 | } |
399 | 406 | ||
407 | + public static JsonObject toJson(ProvisionDeviceResponseMsg payload) { | ||
408 | + return toJson(payload, false, 0); | ||
409 | + } | ||
410 | + | ||
411 | + public static JsonObject toJson(ProvisionDeviceResponseMsg payload, int requestId) { | ||
412 | + return toJson(payload, true, requestId); | ||
413 | + } | ||
414 | + | ||
415 | + private static JsonObject toJson(ProvisionDeviceResponseMsg payload, boolean toGateway, int requestId) { | ||
416 | + JsonObject result = new JsonObject(); | ||
417 | + if (payload.getProvisionResponseStatus() == TransportProtos.ProvisionResponseStatus.NOT_FOUND) { | ||
418 | + result.addProperty("errorMsg", "Provision data was not found!"); | ||
419 | + result.addProperty("provisionDeviceStatus", ProvisionResponseStatus.NOT_FOUND.name()); | ||
420 | + } else if (payload.getProvisionResponseStatus() == TransportProtos.ProvisionResponseStatus.FAILURE) { | ||
421 | + result.addProperty("errorMsg", "Failed to provision device!"); | ||
422 | + result.addProperty("provisionDeviceStatus", ProvisionResponseStatus.FAILURE.name()); | ||
423 | + } else { | ||
424 | + if (toGateway) { | ||
425 | + result.addProperty("id", requestId); | ||
426 | + } | ||
427 | + result.addProperty("deviceId", new UUID(payload.getDeviceCredentials().getDeviceIdMSB(), payload.getDeviceCredentials().getDeviceIdLSB()).toString()); | ||
428 | + result.addProperty("credentialsType", payload.getDeviceCredentials().getCredentialsType().name()); | ||
429 | + result.addProperty("credentialsId", payload.getDeviceCredentials().getCredentialsId()); | ||
430 | + result.addProperty("credentialsValue", | ||
431 | + StringUtils.isEmpty(payload.getDeviceCredentials().getCredentialsValue()) ? null : payload.getDeviceCredentials().getCredentialsValue()); | ||
432 | + result.addProperty("provisionDeviceStatus", ProvisionResponseStatus.SUCCESS.name()); | ||
433 | + } | ||
434 | + return result; | ||
435 | + } | ||
436 | + | ||
400 | public static JsonElement toErrorJson(String errorMsg) { | 437 | public static JsonElement toErrorJson(String errorMsg) { |
401 | JsonObject error = new JsonObject(); | 438 | JsonObject error = new JsonObject(); |
402 | error.addProperty("error", errorMsg); | 439 | error.addProperty("error", errorMsg); |
@@ -410,6 +447,13 @@ public class JsonConverter { | @@ -410,6 +447,13 @@ public class JsonConverter { | ||
410 | return result; | 447 | return result; |
411 | } | 448 | } |
412 | 449 | ||
450 | + public static JsonElement toGatewayJson(String deviceName, TransportProtos.ProvisionDeviceResponseMsg responseRequest) { | ||
451 | + JsonObject result = new JsonObject(); | ||
452 | + result.addProperty(DEVICE_PROPERTY, deviceName); | ||
453 | + result.add("data", JsonConverter.toJson(responseRequest)); | ||
454 | + return result; | ||
455 | + } | ||
456 | + | ||
413 | public static Set<AttributeKvEntry> convertToAttributes(JsonElement element) { | 457 | public static Set<AttributeKvEntry> convertToAttributes(JsonElement element) { |
414 | Set<AttributeKvEntry> result = new HashSet<>(); | 458 | Set<AttributeKvEntry> result = new HashSet<>(); |
415 | long ts = System.currentTimeMillis(); | 459 | long ts = System.currentTimeMillis(); |
@@ -498,4 +542,55 @@ public class JsonConverter { | @@ -498,4 +542,55 @@ public class JsonConverter { | ||
498 | maxStringValueLength = length; | 542 | maxStringValueLength = length; |
499 | } | 543 | } |
500 | 544 | ||
545 | + public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(String json) { | ||
546 | + JsonElement jsonElement = new JsonParser().parse(json); | ||
547 | + if (jsonElement.isJsonObject()) { | ||
548 | + return buildProvisionRequestMsg(jsonElement.getAsJsonObject()); | ||
549 | + } else { | ||
550 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); | ||
551 | + } | ||
552 | + } | ||
553 | + | ||
554 | + public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(JsonObject jo) { | ||
555 | + return buildProvisionRequestMsg(jo); | ||
556 | + } | ||
557 | + | ||
558 | + private static TransportProtos.ProvisionDeviceRequestMsg buildProvisionRequestMsg(JsonObject jo) { | ||
559 | + return TransportProtos.ProvisionDeviceRequestMsg.newBuilder() | ||
560 | + .setDeviceName(getStrValue(jo, DataConstants.DEVICE_NAME, true)) | ||
561 | + .setCredentialsType(jo.get(DataConstants.CREDENTIALS_TYPE) != null ? TransportProtos.CredentialsType.valueOf(getStrValue(jo, DataConstants.CREDENTIALS_TYPE, false)) : CredentialsType.ACCESS_TOKEN) | ||
562 | + .setCredentialsDataProto(TransportProtos.CredentialsDataProto.newBuilder() | ||
563 | + .setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken(getStrValue(jo, DataConstants.TOKEN, false)).build()) | ||
564 | + .setValidateBasicMqttCredRequestMsg(ValidateBasicMqttCredRequestMsg.newBuilder() | ||
565 | + .setClientId(getStrValue(jo, DataConstants.CLIENT_ID, false)) | ||
566 | + .setUserName(getStrValue(jo, DataConstants.USERNAME, false)) | ||
567 | + .setPassword(getStrValue(jo, DataConstants.PASSWORD, false)) | ||
568 | + .build()) | ||
569 | + .setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder() | ||
570 | + .setHash(getStrValue(jo, DataConstants.HASH, false)).build()) | ||
571 | + .build()) | ||
572 | + .setProvisionDeviceCredentialsMsg(buildProvisionDeviceCredentialsMsg( | ||
573 | + getStrValue(jo, DataConstants.PROVISION_KEY, true), | ||
574 | + getStrValue(jo, DataConstants.PROVISION_SECRET, true))) | ||
575 | + .build(); | ||
576 | + } | ||
577 | + | ||
578 | + private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) { | ||
579 | + return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder() | ||
580 | + .setProvisionDeviceKey(provisionKey) | ||
581 | + .setProvisionDeviceSecret(provisionSecret) | ||
582 | + .build(); | ||
583 | + } | ||
584 | + | ||
585 | + | ||
586 | + private static String getStrValue(JsonObject jo, String field, boolean requiredField) { | ||
587 | + if (jo.has(field)) { | ||
588 | + return jo.get(field).getAsString(); | ||
589 | + } else { | ||
590 | + if (requiredField) { | ||
591 | + throw new RuntimeException("Failed to find the field " + field + " in JSON body " + jo + "!"); | ||
592 | + } | ||
593 | + return ""; | ||
594 | + } | ||
595 | + } | ||
501 | } | 596 | } |
@@ -130,6 +130,9 @@ public class ProtoConverter { | @@ -130,6 +130,9 @@ public class ProtoConverter { | ||
130 | } | 130 | } |
131 | } | 131 | } |
132 | 132 | ||
133 | + public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(byte[] bytes) throws InvalidProtocolBufferException { | ||
134 | + return TransportProtos.ProvisionDeviceRequestMsg.parseFrom(bytes); | ||
135 | + } | ||
133 | 136 | ||
134 | private static List<TransportProtos.KeyValueProto> validateKeyValueProtos(List<TransportProtos.KeyValueProto> kvList) { | 137 | private static List<TransportProtos.KeyValueProto> validateKeyValueProtos(List<TransportProtos.KeyValueProto> kvList) { |
135 | kvList.forEach(keyValueProto -> { | 138 | kvList.forEach(keyValueProto -> { |
@@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | @@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | ||
34 | import org.thingsboard.server.common.data.id.RuleChainId; | 34 | import org.thingsboard.server.common.data.id.RuleChainId; |
35 | import org.thingsboard.server.common.data.id.TenantId; | 35 | import org.thingsboard.server.common.data.id.TenantId; |
36 | import org.thingsboard.server.common.msg.TbMsg; | 36 | import org.thingsboard.server.common.msg.TbMsg; |
37 | -import org.thingsboard.server.common.msg.TbMsgDataType; | ||
38 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 37 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
39 | import org.thingsboard.server.common.msg.queue.ServiceQueue; | 38 | import org.thingsboard.server.common.msg.queue.ServiceQueue; |
40 | import org.thingsboard.server.common.msg.queue.ServiceType; | 39 | import org.thingsboard.server.common.msg.queue.ServiceType; |
@@ -49,9 +48,10 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; | @@ -49,9 +48,10 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; | ||
49 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; | 48 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; |
50 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | 49 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
51 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | 50 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
52 | -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | ||
53 | import org.thingsboard.server.common.transport.util.JsonUtils; | 51 | import org.thingsboard.server.common.transport.util.JsonUtils; |
54 | import org.thingsboard.server.gen.transport.TransportProtos; | 52 | import org.thingsboard.server.gen.transport.TransportProtos; |
53 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; | ||
54 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | ||
55 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; | 55 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
56 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; | 56 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
57 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; | 57 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
@@ -75,11 +75,9 @@ import org.thingsboard.server.common.stats.StatsType; | @@ -75,11 +75,9 @@ import org.thingsboard.server.common.stats.StatsType; | ||
75 | 75 | ||
76 | import javax.annotation.PostConstruct; | 76 | import javax.annotation.PostConstruct; |
77 | import javax.annotation.PreDestroy; | 77 | import javax.annotation.PreDestroy; |
78 | -import java.util.Arrays; | ||
79 | import java.util.Collections; | 78 | import java.util.Collections; |
80 | import java.util.List; | 79 | import java.util.List; |
81 | import java.util.Map; | 80 | import java.util.Map; |
82 | -import java.util.Optional; | ||
83 | import java.util.Random; | 81 | import java.util.Random; |
84 | import java.util.UUID; | 82 | import java.util.UUID; |
85 | import java.util.concurrent.ConcurrentHashMap; | 83 | import java.util.concurrent.ConcurrentHashMap; |
@@ -91,7 +89,6 @@ import java.util.concurrent.ScheduledExecutorService; | @@ -91,7 +89,6 @@ import java.util.concurrent.ScheduledExecutorService; | ||
91 | import java.util.concurrent.ScheduledFuture; | 89 | import java.util.concurrent.ScheduledFuture; |
92 | import java.util.concurrent.TimeUnit; | 90 | import java.util.concurrent.TimeUnit; |
93 | import java.util.concurrent.atomic.AtomicInteger; | 91 | import java.util.concurrent.atomic.AtomicInteger; |
94 | -import java.util.function.Function; | ||
95 | 92 | ||
96 | /** | 93 | /** |
97 | * Created by ashvayka on 17.10.18. | 94 | * Created by ashvayka on 17.10.18. |
@@ -235,6 +232,11 @@ public class DefaultTransportService implements TransportService { | @@ -235,6 +232,11 @@ public class DefaultTransportService implements TransportService { | ||
235 | } | 232 | } |
236 | 233 | ||
237 | @Override | 234 | @Override |
235 | + public ScheduledExecutorService getSchedulerExecutor(){ | ||
236 | + return this.schedulerExecutor; | ||
237 | + } | ||
238 | + | ||
239 | + @Override | ||
238 | public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { | 240 | public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { |
239 | sessions.putIfAbsent(toSessionId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener)); | 241 | sessions.putIfAbsent(toSessionId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener)); |
240 | } | 242 | } |
@@ -333,6 +335,16 @@ public class DefaultTransportService implements TransportService { | @@ -333,6 +335,16 @@ public class DefaultTransportService implements TransportService { | ||
333 | } | 335 | } |
334 | 336 | ||
335 | @Override | 337 | @Override |
338 | + public void process(ProvisionDeviceRequestMsg requestMsg, TransportServiceCallback<ProvisionDeviceResponseMsg> callback) { | ||
339 | + log.trace("Processing msg: {}", requestMsg); | ||
340 | + TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder().setProvisionDeviceRequestMsg(requestMsg).build()); | ||
341 | + ListenableFuture<ProvisionDeviceResponseMsg> response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> | ||
342 | + tmp.getValue().getProvisionDeviceResponseMsg() | ||
343 | + , MoreExecutors.directExecutor()); | ||
344 | + AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); | ||
345 | + } | ||
346 | + | ||
347 | + @Override | ||
336 | public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) { | 348 | public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) { |
337 | if (log.isTraceEnabled()) { | 349 | if (log.isTraceEnabled()) { |
338 | log.trace("[{}] Processing msg: {}", toSessionId(sessionInfo), msg); | 350 | log.trace("[{}] Processing msg: {}", toSessionId(sessionInfo), msg); |
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; | @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
47 | import org.thingsboard.server.dao.audit.sink.AuditLogSink; | 47 | import org.thingsboard.server.dao.audit.sink.AuditLogSink; |
48 | import org.thingsboard.server.dao.entity.EntityService; | 48 | import org.thingsboard.server.dao.entity.EntityService; |
49 | import org.thingsboard.server.dao.exception.DataValidationException; | 49 | import org.thingsboard.server.dao.exception.DataValidationException; |
50 | +import org.thingsboard.server.dao.device.provision.ProvisionRequest; | ||
50 | import org.thingsboard.server.dao.service.DataValidator; | 51 | import org.thingsboard.server.dao.service.DataValidator; |
51 | 52 | ||
52 | import java.io.PrintWriter; | 53 | import java.io.PrintWriter; |
@@ -257,6 +258,13 @@ public class AuditLogServiceImpl implements AuditLogService { | @@ -257,6 +258,13 @@ public class AuditLogServiceImpl implements AuditLogService { | ||
257 | actionData.put("os", os); | 258 | actionData.put("os", os); |
258 | actionData.put("device", device); | 259 | actionData.put("device", device); |
259 | break; | 260 | break; |
261 | + case PROVISION_SUCCESS: | ||
262 | + case PROVISION_FAILURE: | ||
263 | + ProvisionRequest request = extractParameter(ProvisionRequest.class, additionalInfo); | ||
264 | + if (request != null) { | ||
265 | + actionData.set("provisionRequest", objectMapper.valueToTree(request)); | ||
266 | + } | ||
267 | + break; | ||
260 | } | 268 | } |
261 | return actionData; | 269 | return actionData; |
262 | } | 270 | } |
@@ -214,5 +214,4 @@ public interface DeviceDao extends Dao<Device> { | @@ -214,5 +214,4 @@ public interface DeviceDao extends Dao<Device> { | ||
214 | * @return the list of device objects | 214 | * @return the list of device objects |
215 | */ | 215 | */ |
216 | PageData<Device> findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink); | 216 | PageData<Device> findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink); |
217 | - | ||
218 | } | 217 | } |
@@ -38,5 +38,7 @@ public interface DeviceProfileDao extends Dao<DeviceProfile> { | @@ -38,5 +38,7 @@ public interface DeviceProfileDao extends Dao<DeviceProfile> { | ||
38 | 38 | ||
39 | DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId); | 39 | DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId); |
40 | 40 | ||
41 | + DeviceProfile findByProvisionDeviceKey(String provisionDeviceKey); | ||
42 | + | ||
41 | DeviceProfile findByName(TenantId tenantId, String profileName); | 43 | DeviceProfile findByName(TenantId tenantId, String profileName); |
42 | } | 44 | } |
@@ -26,6 +26,7 @@ import org.springframework.stereotype.Service; | @@ -26,6 +26,7 @@ import org.springframework.stereotype.Service; | ||
26 | import org.thingsboard.server.common.data.Device; | 26 | import org.thingsboard.server.common.data.Device; |
27 | import org.thingsboard.server.common.data.DeviceProfile; | 27 | import org.thingsboard.server.common.data.DeviceProfile; |
28 | import org.thingsboard.server.common.data.DeviceProfileInfo; | 28 | import org.thingsboard.server.common.data.DeviceProfileInfo; |
29 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
29 | import org.thingsboard.server.common.data.DeviceProfileType; | 30 | import org.thingsboard.server.common.data.DeviceProfileType; |
30 | import org.thingsboard.server.common.data.DeviceTransportType; | 31 | import org.thingsboard.server.common.data.DeviceTransportType; |
31 | import org.thingsboard.server.common.data.EntitySubtype; | 32 | import org.thingsboard.server.common.data.EntitySubtype; |
@@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.Tenant; | @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.Tenant; | ||
33 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | 34 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
34 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; | 35 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; |
35 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | 36 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
37 | +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; | ||
36 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 38 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
37 | import org.thingsboard.server.common.data.id.TenantId; | 39 | import org.thingsboard.server.common.data.id.TenantId; |
38 | import org.thingsboard.server.common.data.page.PageData; | 40 | import org.thingsboard.server.common.data.page.PageData; |
@@ -112,6 +114,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -112,6 +114,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
112 | ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); | 114 | ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
113 | if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) { | 115 | if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) { |
114 | throw new DataValidationException("Device profile with such name already exists!"); | 116 | throw new DataValidationException("Device profile with such name already exists!"); |
117 | + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) { | ||
118 | + throw new DataValidationException("Device profile with such provision device key already exists!"); | ||
115 | } else { | 119 | } else { |
116 | throw t; | 120 | throw t; |
117 | } | 121 | } |
@@ -210,12 +214,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -210,12 +214,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
210 | deviceProfile.setName(profileName); | 214 | deviceProfile.setName(profileName); |
211 | deviceProfile.setType(DeviceProfileType.DEFAULT); | 215 | deviceProfile.setType(DeviceProfileType.DEFAULT); |
212 | deviceProfile.setTransportType(DeviceTransportType.DEFAULT); | 216 | deviceProfile.setTransportType(DeviceTransportType.DEFAULT); |
217 | + deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | ||
213 | deviceProfile.setDescription("Default device profile"); | 218 | deviceProfile.setDescription("Default device profile"); |
214 | DeviceProfileData deviceProfileData = new DeviceProfileData(); | 219 | DeviceProfileData deviceProfileData = new DeviceProfileData(); |
215 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | 220 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
216 | DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration(); | 221 | DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration(); |
222 | + DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); | ||
217 | deviceProfileData.setConfiguration(configuration); | 223 | deviceProfileData.setConfiguration(configuration); |
218 | deviceProfileData.setTransportConfiguration(transportConfiguration); | 224 | deviceProfileData.setTransportConfiguration(transportConfiguration); |
225 | + deviceProfileData.setProvisionConfiguration(provisionConfiguration); | ||
219 | deviceProfile.setProfileData(deviceProfileData); | 226 | deviceProfile.setProfileData(deviceProfileData); |
220 | return saveDeviceProfile(deviceProfile); | 227 | return saveDeviceProfile(deviceProfile); |
221 | } | 228 | } |
@@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.EntityType; | @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.EntityType; | ||
40 | import org.thingsboard.server.common.data.EntityView; | 40 | import org.thingsboard.server.common.data.EntityView; |
41 | import org.thingsboard.server.common.data.Tenant; | 41 | import org.thingsboard.server.common.data.Tenant; |
42 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; | 42 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
43 | +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; | ||
43 | import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; | 44 | import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; |
44 | import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; | 45 | import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; |
45 | import org.thingsboard.server.common.data.device.data.DeviceData; | 46 | import org.thingsboard.server.common.data.device.data.DeviceData; |
@@ -57,6 +58,9 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; | @@ -57,6 +58,9 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; | ||
57 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 58 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
58 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; | 59 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
59 | import org.thingsboard.server.dao.customer.CustomerDao; | 60 | import org.thingsboard.server.dao.customer.CustomerDao; |
61 | +import org.thingsboard.server.dao.device.provision.ProvisionFailedException; | ||
62 | +import org.thingsboard.server.dao.device.provision.ProvisionRequest; | ||
63 | +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; | ||
60 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 64 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
61 | import org.thingsboard.server.dao.entityview.EntityViewService; | 65 | import org.thingsboard.server.dao.entityview.EntityViewService; |
62 | import org.thingsboard.server.dao.event.EventService; | 66 | import org.thingsboard.server.dao.event.EventService; |
@@ -64,6 +68,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; | @@ -64,6 +68,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; | ||
64 | import org.thingsboard.server.dao.service.DataValidator; | 68 | import org.thingsboard.server.dao.service.DataValidator; |
65 | import org.thingsboard.server.dao.service.PaginatedRemover; | 69 | import org.thingsboard.server.dao.service.PaginatedRemover; |
66 | import org.thingsboard.server.dao.tenant.TenantDao; | 70 | import org.thingsboard.server.dao.tenant.TenantDao; |
71 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
67 | 72 | ||
68 | import javax.annotation.Nullable; | 73 | import javax.annotation.Nullable; |
69 | import java.util.ArrayList; | 74 | import java.util.ArrayList; |
@@ -466,6 +471,50 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe | @@ -466,6 +471,50 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe | ||
466 | return doSaveDevice(device, null); | 471 | return doSaveDevice(device, null); |
467 | } | 472 | } |
468 | 473 | ||
474 | + @Override | ||
475 | + @CacheEvict(cacheNames = DEVICE_CACHE, key = "{#profile.tenantId, #provisionRequest.deviceName}") | ||
476 | + @Transactional | ||
477 | + public Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile) { | ||
478 | + Device device = new Device(); | ||
479 | + device.setName(provisionRequest.getDeviceName()); | ||
480 | + device.setType(profile.getName()); | ||
481 | + device.setTenantId(profile.getTenantId()); | ||
482 | + Device savedDevice = saveDevice(device); | ||
483 | + if (!StringUtils.isEmpty(provisionRequest.getCredentialsData().getToken()) || | ||
484 | + !StringUtils.isEmpty(provisionRequest.getCredentialsData().getX509CertHash()) || | ||
485 | + !StringUtils.isEmpty(provisionRequest.getCredentialsData().getUsername()) || | ||
486 | + !StringUtils.isEmpty(provisionRequest.getCredentialsData().getPassword()) || | ||
487 | + !StringUtils.isEmpty(provisionRequest.getCredentialsData().getClientId())) { | ||
488 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getTenantId(), savedDevice.getId()); | ||
489 | + if (deviceCredentials == null) { | ||
490 | + deviceCredentials = new DeviceCredentials(); | ||
491 | + } | ||
492 | + deviceCredentials.setDeviceId(savedDevice.getId()); | ||
493 | + deviceCredentials.setCredentialsType(provisionRequest.getCredentialsType()); | ||
494 | + switch (provisionRequest.getCredentialsType()) { | ||
495 | + case ACCESS_TOKEN: | ||
496 | + deviceCredentials.setCredentialsId(provisionRequest.getCredentialsData().getToken()); | ||
497 | + break; | ||
498 | + case MQTT_BASIC: | ||
499 | + BasicMqttCredentials mqttCredentials = new BasicMqttCredentials(); | ||
500 | + mqttCredentials.setClientId(provisionRequest.getCredentialsData().getClientId()); | ||
501 | + mqttCredentials.setUserName(provisionRequest.getCredentialsData().getUsername()); | ||
502 | + mqttCredentials.setPassword(provisionRequest.getCredentialsData().getPassword()); | ||
503 | + deviceCredentials.setCredentialsValue(JacksonUtil.toString(mqttCredentials)); | ||
504 | + break; | ||
505 | + case X509_CERTIFICATE: | ||
506 | + deviceCredentials.setCredentialsValue(provisionRequest.getCredentialsData().getX509CertHash()); | ||
507 | + break; | ||
508 | + } | ||
509 | + try { | ||
510 | + deviceCredentialsService.updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); | ||
511 | + } catch (Exception e) { | ||
512 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
513 | + } | ||
514 | + } | ||
515 | + return savedDevice; | ||
516 | + } | ||
517 | + | ||
469 | private DataValidator<Device> deviceValidator = | 518 | private DataValidator<Device> deviceValidator = |
470 | new DataValidator<Device>() { | 519 | new DataValidator<Device>() { |
471 | 520 |
@@ -169,10 +169,12 @@ public class ModelConstants { | @@ -169,10 +169,12 @@ public class ModelConstants { | ||
169 | public static final String DEVICE_PROFILE_NAME_PROPERTY = "name"; | 169 | public static final String DEVICE_PROFILE_NAME_PROPERTY = "name"; |
170 | public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type"; | 170 | public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type"; |
171 | public static final String DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY = "transport_type"; | 171 | public static final String DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY = "transport_type"; |
172 | + public static final String DEVICE_PROFILE_PROVISION_TYPE_PROPERTY = "provision_type"; | ||
172 | public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data"; | 173 | public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data"; |
173 | public static final String DEVICE_PROFILE_DESCRIPTION_PROPERTY = "description"; | 174 | public static final String DEVICE_PROFILE_DESCRIPTION_PROPERTY = "description"; |
174 | public static final String DEVICE_PROFILE_IS_DEFAULT_PROPERTY = "is_default"; | 175 | public static final String DEVICE_PROFILE_IS_DEFAULT_PROPERTY = "is_default"; |
175 | public static final String DEVICE_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id"; | 176 | public static final String DEVICE_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id"; |
177 | + public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; | ||
176 | 178 | ||
177 | /** | 179 | /** |
178 | * Cassandra entityView constants. | 180 | * Cassandra entityView constants. |
@@ -23,6 +23,7 @@ import org.hibernate.annotations.Type; | @@ -23,6 +23,7 @@ import org.hibernate.annotations.Type; | ||
23 | import org.hibernate.annotations.TypeDef; | 23 | import org.hibernate.annotations.TypeDef; |
24 | import org.thingsboard.server.common.data.DeviceProfile; | 24 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | import org.thingsboard.server.common.data.DeviceProfileType; | 25 | import org.thingsboard.server.common.data.DeviceProfileType; |
26 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
26 | import org.thingsboard.server.common.data.DeviceTransportType; | 27 | import org.thingsboard.server.common.data.DeviceTransportType; |
27 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | 28 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
28 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 29 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
@@ -62,6 +63,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | @@ -62,6 +63,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | ||
62 | @Column(name = ModelConstants.DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY) | 63 | @Column(name = ModelConstants.DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY) |
63 | private DeviceTransportType transportType; | 64 | private DeviceTransportType transportType; |
64 | 65 | ||
66 | + @Enumerated(EnumType.STRING) | ||
67 | + @Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_TYPE_PROPERTY) | ||
68 | + private DeviceProfileProvisionType provisionType; | ||
69 | + | ||
65 | @Column(name = ModelConstants.DEVICE_PROFILE_DESCRIPTION_PROPERTY) | 70 | @Column(name = ModelConstants.DEVICE_PROFILE_DESCRIPTION_PROPERTY) |
66 | private String description; | 71 | private String description; |
67 | 72 | ||
@@ -78,6 +83,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | @@ -78,6 +83,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | ||
78 | @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") | 83 | @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") |
79 | private JsonNode profileData; | 84 | private JsonNode profileData; |
80 | 85 | ||
86 | + @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) | ||
87 | + private String provisionDeviceKey; | ||
88 | + | ||
81 | public DeviceProfileEntity() { | 89 | public DeviceProfileEntity() { |
82 | super(); | 90 | super(); |
83 | } | 91 | } |
@@ -93,12 +101,14 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | @@ -93,12 +101,14 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | ||
93 | this.name = deviceProfile.getName(); | 101 | this.name = deviceProfile.getName(); |
94 | this.type = deviceProfile.getType(); | 102 | this.type = deviceProfile.getType(); |
95 | this.transportType = deviceProfile.getTransportType(); | 103 | this.transportType = deviceProfile.getTransportType(); |
104 | + this.provisionType = deviceProfile.getProvisionType(); | ||
96 | this.description = deviceProfile.getDescription(); | 105 | this.description = deviceProfile.getDescription(); |
97 | this.isDefault = deviceProfile.isDefault(); | 106 | this.isDefault = deviceProfile.isDefault(); |
98 | this.profileData = JacksonUtil.convertValue(deviceProfile.getProfileData(), ObjectNode.class); | 107 | this.profileData = JacksonUtil.convertValue(deviceProfile.getProfileData(), ObjectNode.class); |
99 | if (deviceProfile.getDefaultRuleChainId() != null) { | 108 | if (deviceProfile.getDefaultRuleChainId() != null) { |
100 | this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId().getId(); | 109 | this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId().getId(); |
101 | } | 110 | } |
111 | + this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); | ||
102 | } | 112 | } |
103 | 113 | ||
104 | @Override | 114 | @Override |
@@ -125,12 +135,14 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | @@ -125,12 +135,14 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl | ||
125 | deviceProfile.setName(name); | 135 | deviceProfile.setName(name); |
126 | deviceProfile.setType(type); | 136 | deviceProfile.setType(type); |
127 | deviceProfile.setTransportType(transportType); | 137 | deviceProfile.setTransportType(transportType); |
138 | + deviceProfile.setProvisionType(provisionType); | ||
128 | deviceProfile.setDescription(description); | 139 | deviceProfile.setDescription(description); |
129 | deviceProfile.setDefault(isDefault); | 140 | deviceProfile.setDefault(isDefault); |
130 | deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class)); | 141 | deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class)); |
131 | if (defaultRuleChainId != null) { | 142 | if (defaultRuleChainId != null) { |
132 | deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId)); | 143 | deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId)); |
133 | } | 144 | } |
145 | + deviceProfile.setProvisionDeviceKey(provisionDeviceKey); | ||
134 | return deviceProfile; | 146 | return deviceProfile; |
135 | } | 147 | } |
136 | } | 148 | } |
@@ -20,7 +20,6 @@ import org.springframework.data.domain.Pageable; | @@ -20,7 +20,6 @@ import org.springframework.data.domain.Pageable; | ||
20 | import org.springframework.data.jpa.repository.Query; | 20 | import org.springframework.data.jpa.repository.Query; |
21 | import org.springframework.data.repository.PagingAndSortingRepository; | 21 | import org.springframework.data.repository.PagingAndSortingRepository; |
22 | import org.springframework.data.repository.query.Param; | 22 | import org.springframework.data.repository.query.Param; |
23 | -import org.thingsboard.server.common.data.DeviceProfile; | ||
24 | import org.thingsboard.server.common.data.DeviceProfileInfo; | 23 | import org.thingsboard.server.common.data.DeviceProfileInfo; |
25 | import org.thingsboard.server.common.data.DeviceTransportType; | 24 | import org.thingsboard.server.common.data.DeviceTransportType; |
26 | import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; | 25 | import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; |
@@ -66,4 +65,5 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi | @@ -66,4 +65,5 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi | ||
66 | 65 | ||
67 | DeviceProfileEntity findByTenantIdAndName(UUID id, String profileName); | 66 | DeviceProfileEntity findByTenantIdAndName(UUID id, String profileName); |
68 | 67 | ||
68 | + DeviceProfileEntity findByProvisionDeviceKey(@Param("provisionDeviceKey") String provisionDeviceKey); | ||
69 | } | 69 | } |
@@ -168,5 +168,4 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit | @@ -168,5 +168,4 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit | ||
168 | DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id); | 168 | DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id); |
169 | 169 | ||
170 | Long countByDeviceProfileId(UUID deviceProfileId); | 170 | Long countByDeviceProfileId(UUID deviceProfileId); |
171 | - | ||
172 | } | 171 | } |
@@ -92,6 +92,11 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE | @@ -92,6 +92,11 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE | ||
92 | } | 92 | } |
93 | 93 | ||
94 | @Override | 94 | @Override |
95 | + public DeviceProfile findByProvisionDeviceKey(String provisionDeviceKey) { | ||
96 | + return DaoUtil.getData(deviceProfileRepository.findByProvisionDeviceKey(provisionDeviceKey)); | ||
97 | + } | ||
98 | + | ||
99 | + @Override | ||
95 | public DeviceProfile findByName(TenantId tenantId, String profileName) { | 100 | public DeviceProfile findByName(TenantId tenantId, String profileName) { |
96 | return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName)); | 101 | return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName)); |
97 | } | 102 | } |
@@ -163,13 +163,16 @@ CREATE TABLE IF NOT EXISTS device_profile ( | @@ -163,13 +163,16 @@ CREATE TABLE IF NOT EXISTS device_profile ( | ||
163 | name varchar(255), | 163 | name varchar(255), |
164 | type varchar(255), | 164 | type varchar(255), |
165 | transport_type varchar(255), | 165 | transport_type varchar(255), |
166 | + provision_type varchar(255), | ||
166 | profile_data jsonb, | 167 | profile_data jsonb, |
167 | description varchar, | 168 | description varchar, |
168 | search_text varchar(255), | 169 | search_text varchar(255), |
169 | is_default boolean, | 170 | is_default boolean, |
170 | tenant_id uuid, | 171 | tenant_id uuid, |
171 | default_rule_chain_id uuid, | 172 | default_rule_chain_id uuid, |
173 | + provision_device_key varchar, | ||
172 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), | 174 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
175 | + CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), | ||
173 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) | 176 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) |
174 | ); | 177 | ); |
175 | 178 |
@@ -181,13 +181,16 @@ CREATE TABLE IF NOT EXISTS device_profile ( | @@ -181,13 +181,16 @@ CREATE TABLE IF NOT EXISTS device_profile ( | ||
181 | name varchar(255), | 181 | name varchar(255), |
182 | type varchar(255), | 182 | type varchar(255), |
183 | transport_type varchar(255), | 183 | transport_type varchar(255), |
184 | + provision_type varchar(255), | ||
184 | profile_data jsonb, | 185 | profile_data jsonb, |
185 | description varchar, | 186 | description varchar, |
186 | search_text varchar(255), | 187 | search_text varchar(255), |
187 | is_default boolean, | 188 | is_default boolean, |
188 | tenant_id uuid, | 189 | tenant_id uuid, |
189 | default_rule_chain_id uuid, | 190 | default_rule_chain_id uuid, |
191 | + provision_device_key varchar, | ||
190 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), | 192 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
193 | + CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), | ||
191 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) | 194 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) |
192 | ); | 195 | ); |
193 | 196 |
@@ -105,6 +105,7 @@ import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-conditio | @@ -105,6 +105,7 @@ import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-conditio | ||
105 | import { FilterTextComponent } from './filter/filter-text.component'; | 105 | import { FilterTextComponent } from './filter/filter-text.component'; |
106 | import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component'; | 106 | import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component'; |
107 | import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component'; | 107 | import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component'; |
108 | +import { DeviceProfileProvisionConfigurationComponent } from "./profile/device-profile-provision-configuration.component"; | ||
108 | import { AlarmScheduleComponent } from './profile/alarm/alarm-schedule.component'; | 109 | import { AlarmScheduleComponent } from './profile/alarm/alarm-schedule.component'; |
109 | import { DeviceWizardDialogComponent } from './wizard/device-wizard-dialog.component'; | 110 | import { DeviceWizardDialogComponent } from './wizard/device-wizard-dialog.component'; |
110 | import { DeviceCredentialsComponent } from './device/device-credentials.component'; | 111 | import { DeviceCredentialsComponent } from './device/device-credentials.component'; |
@@ -202,6 +203,7 @@ import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alar | @@ -202,6 +203,7 @@ import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alar | ||
202 | AddDeviceProfileDialogComponent, | 203 | AddDeviceProfileDialogComponent, |
203 | RuleChainAutocompleteComponent, | 204 | RuleChainAutocompleteComponent, |
204 | AlarmScheduleInfoComponent, | 205 | AlarmScheduleInfoComponent, |
206 | + DeviceProfileProvisionConfigurationComponent, | ||
205 | AlarmScheduleComponent, | 207 | AlarmScheduleComponent, |
206 | DeviceWizardDialogComponent, | 208 | DeviceWizardDialogComponent, |
207 | DeviceCredentialsComponent, | 209 | DeviceCredentialsComponent, |
@@ -290,7 +292,9 @@ import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alar | @@ -290,7 +292,9 @@ import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alar | ||
290 | AlarmScheduleInfoComponent, | 292 | AlarmScheduleInfoComponent, |
291 | AlarmScheduleComponent, | 293 | AlarmScheduleComponent, |
292 | AlarmScheduleDialogComponent, | 294 | AlarmScheduleDialogComponent, |
293 | - EditAlarmDetailsDialogComponent | 295 | + EditAlarmDetailsDialogComponent, |
296 | + DeviceProfileProvisionConfigurationComponent, | ||
297 | + AlarmScheduleComponent | ||
294 | ], | 298 | ], |
295 | providers: [ | 299 | providers: [ |
296 | WidgetComponentService, | 300 | WidgetComponentService, |
@@ -96,6 +96,14 @@ | @@ -96,6 +96,14 @@ | ||
96 | </tb-device-profile-alarms> | 96 | </tb-device-profile-alarms> |
97 | </form> | 97 | </form> |
98 | </mat-step> | 98 | </mat-step> |
99 | + <mat-step [stepControl]="provisionConfigFormGroup" [optional]="true"> | ||
100 | + <form [formGroup]="provisionConfigFormGroup" style="padding-bottom: 16px;"> | ||
101 | + <ng-template matStepLabel>{{ 'device-profile.device-provisioning' | translate }}</ng-template> | ||
102 | + <tb-device-profile-provision-configuration | ||
103 | + formControlName="provisionConfiguration"> | ||
104 | + </tb-device-profile-provision-configuration> | ||
105 | + </form> | ||
106 | + </mat-step> | ||
99 | </mat-horizontal-stepper> | 107 | </mat-horizontal-stepper> |
100 | </div> | 108 | </div> |
101 | <div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;"> | 109 | <div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;"> |
@@ -36,7 +36,10 @@ import { | @@ -36,7 +36,10 @@ import { | ||
36 | DeviceProfile, | 36 | DeviceProfile, |
37 | DeviceProfileType, | 37 | DeviceProfileType, |
38 | deviceProfileTypeTranslationMap, | 38 | deviceProfileTypeTranslationMap, |
39 | - DeviceTransportType, deviceTransportTypeHintMap, | 39 | + DeviceProvisionConfiguration, |
40 | + DeviceProvisionType, | ||
41 | + DeviceTransportType, | ||
42 | + deviceTransportTypeHintMap, | ||
40 | deviceTransportTypeTranslationMap | 43 | deviceTransportTypeTranslationMap |
41 | } from '@shared/models/device.models'; | 44 | } from '@shared/models/device.models'; |
42 | import { DeviceProfileService } from '@core/http/device-profile.service'; | 45 | import { DeviceProfileService } from '@core/http/device-profile.service'; |
@@ -84,6 +87,8 @@ export class AddDeviceProfileDialogComponent extends | @@ -84,6 +87,8 @@ export class AddDeviceProfileDialogComponent extends | ||
84 | 87 | ||
85 | alarmRulesFormGroup: FormGroup; | 88 | alarmRulesFormGroup: FormGroup; |
86 | 89 | ||
90 | + provisionConfigFormGroup: FormGroup; | ||
91 | + | ||
87 | constructor(protected store: Store<AppState>, | 92 | constructor(protected store: Store<AppState>, |
88 | protected router: Router, | 93 | protected router: Router, |
89 | @Inject(MAT_DIALOG_DATA) public data: AddDeviceProfileDialogData, | 94 | @Inject(MAT_DIALOG_DATA) public data: AddDeviceProfileDialogData, |
@@ -118,6 +123,14 @@ export class AddDeviceProfileDialogComponent extends | @@ -118,6 +123,14 @@ export class AddDeviceProfileDialogComponent extends | ||
118 | alarms: [null] | 123 | alarms: [null] |
119 | } | 124 | } |
120 | ); | 125 | ); |
126 | + | ||
127 | + this.provisionConfigFormGroup = this.fb.group( | ||
128 | + { | ||
129 | + provisionConfiguration: [{ | ||
130 | + type: DeviceProvisionType.DISABLED | ||
131 | + } as DeviceProvisionConfiguration, [Validators.required]] | ||
132 | + } | ||
133 | + ); | ||
121 | } | 134 | } |
122 | 135 | ||
123 | private deviceProfileTransportTypeChanged() { | 136 | private deviceProfileTransportTypeChanged() { |
@@ -138,7 +151,7 @@ export class AddDeviceProfileDialogComponent extends | @@ -138,7 +151,7 @@ export class AddDeviceProfileDialogComponent extends | ||
138 | } | 151 | } |
139 | 152 | ||
140 | nextStep() { | 153 | nextStep() { |
141 | - if (this.selectedIndex < 2) { | 154 | + if (this.selectedIndex < 3) { |
142 | this.addDeviceProfileStepper.next(); | 155 | this.addDeviceProfileStepper.next(); |
143 | } else { | 156 | } else { |
144 | this.add(); | 157 | this.add(); |
@@ -153,20 +166,28 @@ export class AddDeviceProfileDialogComponent extends | @@ -153,20 +166,28 @@ export class AddDeviceProfileDialogComponent extends | ||
153 | return this.transportConfigFormGroup; | 166 | return this.transportConfigFormGroup; |
154 | case 2: | 167 | case 2: |
155 | return this.alarmRulesFormGroup; | 168 | return this.alarmRulesFormGroup; |
169 | + case 3: | ||
170 | + return this.provisionConfigFormGroup; | ||
156 | } | 171 | } |
157 | } | 172 | } |
158 | 173 | ||
159 | add(): void { | 174 | add(): void { |
160 | if (this.allValid()) { | 175 | if (this.allValid()) { |
176 | + const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value; | ||
177 | + const provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey; | ||
178 | + delete deviceProvisionConfiguration.provisionDeviceKey; | ||
161 | const deviceProfile: DeviceProfile = { | 179 | const deviceProfile: DeviceProfile = { |
162 | name: this.deviceProfileDetailsFormGroup.get('name').value, | 180 | name: this.deviceProfileDetailsFormGroup.get('name').value, |
163 | type: this.deviceProfileDetailsFormGroup.get('type').value, | 181 | type: this.deviceProfileDetailsFormGroup.get('type').value, |
164 | transportType: this.transportConfigFormGroup.get('transportType').value, | 182 | transportType: this.transportConfigFormGroup.get('transportType').value, |
183 | + provisionType: deviceProvisionConfiguration.type, | ||
184 | + provisionDeviceKey, | ||
165 | description: this.deviceProfileDetailsFormGroup.get('description').value, | 185 | description: this.deviceProfileDetailsFormGroup.get('description').value, |
166 | profileData: { | 186 | profileData: { |
167 | configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), | 187 | configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), |
168 | transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, | 188 | transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, |
169 | - alarms: this.alarmRulesFormGroup.get('alarms').value | 189 | + alarms: this.alarmRulesFormGroup.get('alarms').value, |
190 | + provisionConfiguration: deviceProvisionConfiguration | ||
170 | } | 191 | } |
171 | }; | 192 | }; |
172 | if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) { | 193 | if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) { |
@@ -188,6 +209,8 @@ export class AddDeviceProfileDialogComponent extends | @@ -188,6 +209,8 @@ export class AddDeviceProfileDialogComponent extends | ||
188 | return 'device-profile.transport-configuration'; | 209 | return 'device-profile.transport-configuration'; |
189 | case 2: | 210 | case 2: |
190 | return 'device-profile.alarm-rules'; | 211 | return 'device-profile.alarm-rules'; |
212 | + case 3: | ||
213 | + return 'device-profile.device-provisioning'; | ||
191 | } | 214 | } |
192 | } | 215 | } |
193 | 216 |
ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<div [formGroup]="provisionConfigurationFormGroup"> | ||
19 | + <mat-form-field class="mat-block"> | ||
20 | + <mat-label translate>device-profile.provision-strategy</mat-label> | ||
21 | + <mat-select formControlName="type" required> | ||
22 | + <mat-option *ngFor="let type of deviceProvisionTypes" [value]="type"> | ||
23 | + {{deviceProvisionTypeTranslateMap.get(type) | translate}} | ||
24 | + </mat-option> | ||
25 | + </mat-select> | ||
26 | + <mat-error *ngIf="provisionConfigurationFormGroup.get('type').hasError('required')"> | ||
27 | + {{ 'device-profile.provision-strategy-required' | translate }} | ||
28 | + </mat-error> | ||
29 | + </mat-form-field> | ||
30 | + <section *ngIf="provisionConfigurationFormGroup.get('type').value !== deviceProvisionType.DISABLED" fxLayoutGap.gt-xs="8px" fxLayout="row" fxLayout.xs="column"> | ||
31 | + <mat-form-field fxFlex class="mat-block"> | ||
32 | + <mat-label translate>device-profile.provision-device-key</mat-label> | ||
33 | + <input matInput formControlName="provisionDeviceKey" required/> | ||
34 | + <mat-error *ngIf="provisionConfigurationFormGroup.get('provisionDeviceKey').hasError('required')"> | ||
35 | + {{ 'device-profile.provision-device-key-required' | translate }} | ||
36 | + </mat-error> | ||
37 | + </mat-form-field> | ||
38 | + <mat-form-field fxFlex class="mat-block"> | ||
39 | + <mat-label translate>device-profile.provision-device-secret</mat-label> | ||
40 | + <input matInput formControlName="provisionDeviceSecret" required/> | ||
41 | + <mat-error *ngIf="provisionConfigurationFormGroup.get('provisionDeviceSecret').hasError('required')"> | ||
42 | + {{ 'device-profile.provision-device-secret-required' | translate }} | ||
43 | + </mat-error> | ||
44 | + </mat-form-field> | ||
45 | + </section> | ||
46 | +</div> |
ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { Component, forwardRef, Input, OnInit } from "@angular/core"; | ||
18 | +import { | ||
19 | + ControlValueAccessor, | ||
20 | + FormBuilder, | ||
21 | + FormControl, | ||
22 | + FormGroup, | ||
23 | + NG_VALIDATORS, | ||
24 | + NG_VALUE_ACCESSOR, | ||
25 | + ValidationErrors, | ||
26 | + Validator, | ||
27 | + Validators | ||
28 | +} from "@angular/forms"; | ||
29 | +import { coerceBooleanProperty } from "@angular/cdk/coercion"; | ||
30 | +import { | ||
31 | + DeviceProvisionConfiguration, | ||
32 | + DeviceProvisionType, | ||
33 | + deviceProvisionTypeTranslationMap | ||
34 | +} from "@shared/models/device.models"; | ||
35 | +import { isDefinedAndNotNull } from "@core/utils"; | ||
36 | + | ||
37 | +@Component({ | ||
38 | + selector: 'tb-device-profile-provision-configuration', | ||
39 | + templateUrl: './device-profile-provision-configuration.component.html', | ||
40 | + styleUrls: [], | ||
41 | + providers: [ | ||
42 | + { | ||
43 | + provide: NG_VALUE_ACCESSOR, | ||
44 | + useExisting: forwardRef(() => DeviceProfileProvisionConfigurationComponent), | ||
45 | + multi: true | ||
46 | + }, | ||
47 | + { | ||
48 | + provide: NG_VALIDATORS, | ||
49 | + useExisting: forwardRef(() => DeviceProfileProvisionConfigurationComponent), | ||
50 | + multi: true, | ||
51 | + } | ||
52 | + ] | ||
53 | +}) | ||
54 | +export class DeviceProfileProvisionConfigurationComponent implements ControlValueAccessor, OnInit, Validator { | ||
55 | + | ||
56 | + provisionConfigurationFormGroup: FormGroup; | ||
57 | + | ||
58 | + deviceProvisionType = DeviceProvisionType; | ||
59 | + deviceProvisionTypes = Object.keys(DeviceProvisionType); | ||
60 | + deviceProvisionTypeTranslateMap = deviceProvisionTypeTranslationMap; | ||
61 | + | ||
62 | + private requiredValue: boolean; | ||
63 | + get required(): boolean { | ||
64 | + return this.requiredValue; | ||
65 | + } | ||
66 | + @Input() | ||
67 | + set required(value: boolean) { | ||
68 | + this.requiredValue = coerceBooleanProperty(value); | ||
69 | + } | ||
70 | + | ||
71 | + @Input() | ||
72 | + disabled: boolean; | ||
73 | + | ||
74 | + private propagateChange = (v: any) => { }; | ||
75 | + | ||
76 | + constructor(private fb: FormBuilder) { | ||
77 | + } | ||
78 | + | ||
79 | + ngOnInit(): void { | ||
80 | + this.provisionConfigurationFormGroup = this.fb.group({ | ||
81 | + type: [DeviceProvisionType.DISABLED, Validators.required], | ||
82 | + provisionDeviceSecret: [{value: null, disabled: true}, Validators.required], | ||
83 | + provisionDeviceKey: [{value: null, disabled: true}, Validators.required] | ||
84 | + }); | ||
85 | + this.provisionConfigurationFormGroup.get('type').valueChanges.subscribe((type) => { | ||
86 | + if (type === DeviceProvisionType.DISABLED) { | ||
87 | + this.provisionConfigurationFormGroup.get('provisionDeviceSecret').disable({emitEvent: false}); | ||
88 | + this.provisionConfigurationFormGroup.get('provisionDeviceSecret').patchValue(null,{emitEvent: false}); | ||
89 | + this.provisionConfigurationFormGroup.get('provisionDeviceKey').disable({emitEvent: false}); | ||
90 | + this.provisionConfigurationFormGroup.get('provisionDeviceKey').patchValue(null); | ||
91 | + } else { | ||
92 | + this.provisionConfigurationFormGroup.get('provisionDeviceSecret').enable({emitEvent: false}); | ||
93 | + this.provisionConfigurationFormGroup.get('provisionDeviceKey').enable({emitEvent: false}); | ||
94 | + } | ||
95 | + }); | ||
96 | + this.provisionConfigurationFormGroup.valueChanges.subscribe(() => { | ||
97 | + this.updateModel(); | ||
98 | + }); | ||
99 | + } | ||
100 | + | ||
101 | + registerOnChange(fn: any): void { | ||
102 | + this.propagateChange = fn; | ||
103 | + } | ||
104 | + | ||
105 | + registerOnTouched(fn: any): void { | ||
106 | + } | ||
107 | + | ||
108 | + writeValue(value: DeviceProvisionConfiguration | null): void { | ||
109 | + if (isDefinedAndNotNull(value)){ | ||
110 | + this.provisionConfigurationFormGroup.patchValue(value, {emitEvent: false}); | ||
111 | + } else { | ||
112 | + this.provisionConfigurationFormGroup.patchValue({type: DeviceProvisionType.DISABLED}); | ||
113 | + } | ||
114 | + } | ||
115 | + | ||
116 | + setDisabledState(isDisabled: boolean){ | ||
117 | + this.disabled = isDisabled; | ||
118 | + if (this.disabled){ | ||
119 | + this.provisionConfigurationFormGroup.disable(); | ||
120 | + } else { | ||
121 | + this.provisionConfigurationFormGroup.enable({emitEvent: false}); | ||
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + validate(c: FormControl): ValidationErrors | null { | ||
126 | + return (this.provisionConfigurationFormGroup.valid) ? null : { | ||
127 | + provisionConfiguration: { | ||
128 | + valid: false, | ||
129 | + }, | ||
130 | + }; | ||
131 | + } | ||
132 | + | ||
133 | + private updateModel(): void { | ||
134 | + let deviceProvisionConfiguration: DeviceProvisionConfiguration = null; | ||
135 | + if (this.provisionConfigurationFormGroup.valid) { | ||
136 | + deviceProvisionConfiguration = this.provisionConfigurationFormGroup.getRawValue(); | ||
137 | + } | ||
138 | + this.propagateChange(deviceProvisionConfiguration); | ||
139 | + } | ||
140 | +} |
@@ -24,13 +24,15 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod | @@ -24,13 +24,15 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod | ||
24 | import { EntityComponent } from '../entity/entity.component'; | 24 | import { EntityComponent } from '../entity/entity.component'; |
25 | import { | 25 | import { |
26 | createDeviceProfileConfiguration, | 26 | createDeviceProfileConfiguration, |
27 | + createDeviceProfileTransportConfiguration, | ||
27 | DeviceProfile, | 28 | DeviceProfile, |
28 | DeviceProfileData, | 29 | DeviceProfileData, |
29 | DeviceProfileType, | 30 | DeviceProfileType, |
30 | deviceProfileTypeTranslationMap, | 31 | deviceProfileTypeTranslationMap, |
32 | + DeviceProvisionConfiguration, | ||
33 | + DeviceProvisionType, | ||
31 | DeviceTransportType, | 34 | DeviceTransportType, |
32 | - deviceTransportTypeTranslationMap, | ||
33 | - createDeviceProfileTransportConfiguration | 35 | + deviceTransportTypeTranslationMap |
34 | } from '@shared/models/device.models'; | 36 | } from '@shared/models/device.models'; |
35 | import { EntityType } from '@shared/models/entity-type.models'; | 37 | import { EntityType } from '@shared/models/entity-type.models'; |
36 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; | 38 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
@@ -72,15 +74,23 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -72,15 +74,23 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
72 | } | 74 | } |
73 | 75 | ||
74 | buildForm(entity: DeviceProfile): FormGroup { | 76 | buildForm(entity: DeviceProfile): FormGroup { |
77 | + const deviceProvisionConfiguration: DeviceProvisionConfiguration = { | ||
78 | + type: entity?.provisionType ? entity?.provisionType : DeviceProvisionType.DISABLED, | ||
79 | + provisionDeviceKey: entity?.provisionDeviceKey, | ||
80 | + provisionDeviceSecret: entity?.profileData?.provisionConfiguration?.provisionDeviceSecret | ||
81 | + }; | ||
75 | const form = this.fb.group( | 82 | const form = this.fb.group( |
76 | { | 83 | { |
77 | name: [entity ? entity.name : '', [Validators.required]], | 84 | name: [entity ? entity.name : '', [Validators.required]], |
78 | type: [entity ? entity.type : null, [Validators.required]], | 85 | type: [entity ? entity.type : null, [Validators.required]], |
79 | transportType: [entity ? entity.transportType : null, [Validators.required]], | 86 | transportType: [entity ? entity.transportType : null, [Validators.required]], |
87 | + provisionType: [deviceProvisionConfiguration.type, [Validators.required]], | ||
88 | + provisionDeviceKey: [deviceProvisionConfiguration.provisionDeviceKey], | ||
80 | profileData: this.fb.group({ | 89 | profileData: this.fb.group({ |
81 | configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required], | 90 | configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required], |
82 | transportConfiguration: [entity && !this.isAdd ? entity.profileData?.transportConfiguration : {}, Validators.required], | 91 | transportConfiguration: [entity && !this.isAdd ? entity.profileData?.transportConfiguration : {}, Validators.required], |
83 | - alarms: [entity && !this.isAdd ? entity.profileData?.alarms : []] | 92 | + alarms: [entity && !this.isAdd ? entity.profileData?.alarms : []], |
93 | + provisionConfiguration: [deviceProvisionConfiguration, Validators.required] | ||
84 | }), | 94 | }), |
85 | defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], | 95 | defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], |
86 | description: [entity ? entity.description : '', []], | 96 | description: [entity ? entity.description : '', []], |
@@ -100,6 +110,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -100,6 +110,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
100 | if (entity && !entity.id) { | 110 | if (entity && !entity.id) { |
101 | form.get('type').patchValue(DeviceProfileType.DEFAULT, {emitEvent: true}); | 111 | form.get('type').patchValue(DeviceProfileType.DEFAULT, {emitEvent: true}); |
102 | form.get('transportType').patchValue(DeviceTransportType.DEFAULT, {emitEvent: true}); | 112 | form.get('transportType').patchValue(DeviceTransportType.DEFAULT, {emitEvent: true}); |
113 | + form.get('provisionType').patchValue(DeviceProvisionType.DISABLED, {emitEvent: true}); | ||
103 | } | 114 | } |
104 | } | 115 | } |
105 | 116 | ||
@@ -130,10 +141,22 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -130,10 +141,22 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
130 | } | 141 | } |
131 | 142 | ||
132 | updateForm(entity: DeviceProfile) { | 143 | updateForm(entity: DeviceProfile) { |
144 | + const deviceProvisionConfiguration: DeviceProvisionConfiguration = { | ||
145 | + type: entity?.provisionType ? entity?.provisionType : DeviceProvisionType.DISABLED, | ||
146 | + provisionDeviceKey: entity?.provisionDeviceKey, | ||
147 | + provisionDeviceSecret: entity?.profileData?.provisionConfiguration?.provisionDeviceSecret | ||
148 | + }; | ||
133 | this.entityForm.patchValue({name: entity.name}); | 149 | this.entityForm.patchValue({name: entity.name}); |
134 | this.entityForm.patchValue({type: entity.type}, {emitEvent: false}); | 150 | this.entityForm.patchValue({type: entity.type}, {emitEvent: false}); |
135 | this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false}); | 151 | this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false}); |
136 | - this.entityForm.patchValue({profileData: entity.profileData}); | 152 | + this.entityForm.patchValue({provisionType: entity.provisionType}, {emitEvent: false}); |
153 | + this.entityForm.patchValue({provisionDeviceKey: entity.provisionDeviceKey}, {emitEvent: false}); | ||
154 | + this.entityForm.patchValue({profileData: { | ||
155 | + configuration: entity.profileData?.configuration, | ||
156 | + transportConfiguration: entity.profileData?.transportConfiguration, | ||
157 | + alarms: entity.profileData?.alarms, | ||
158 | + provisionConfiguration: deviceProvisionConfiguration | ||
159 | + }}); | ||
137 | this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}); | 160 | this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}); |
138 | this.entityForm.patchValue({description: entity.description}); | 161 | this.entityForm.patchValue({description: entity.description}); |
139 | } | 162 | } |
@@ -142,6 +165,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -142,6 +165,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
142 | if (formValue.defaultRuleChainId) { | 165 | if (formValue.defaultRuleChainId) { |
143 | formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId); | 166 | formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId); |
144 | } | 167 | } |
168 | + const deviceProvisionConfiguration: DeviceProvisionConfiguration = formValue.profileData.provisionConfiguration; | ||
169 | + formValue.provisionType = deviceProvisionConfiguration.type; | ||
170 | + formValue.provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey; | ||
171 | + delete deviceProvisionConfiguration.provisionDeviceKey; | ||
145 | return super.prepareFormValue(formValue); | 172 | return super.prepareFormValue(formValue); |
146 | } | 173 | } |
147 | 174 |
@@ -121,6 +121,14 @@ | @@ -121,6 +121,14 @@ | ||
121 | </tb-device-profile-alarms> | 121 | </tb-device-profile-alarms> |
122 | </form> | 122 | </form> |
123 | </mat-step> | 123 | </mat-step> |
124 | + <mat-step [stepControl]="provisionConfigFormGroup" [optional]="true" *ngIf="createProfile"> | ||
125 | + <form [formGroup]="provisionConfigFormGroup" style="padding-bottom: 16px;"> | ||
126 | + <ng-template matStepLabel>{{ 'device-profile.device-provisioning' | translate }}</ng-template> | ||
127 | + <tb-device-profile-provision-configuration | ||
128 | + formControlName="provisionConfiguration"> | ||
129 | + </tb-device-profile-provision-configuration> | ||
130 | + </form> | ||
131 | + </mat-step> | ||
124 | <mat-step [stepControl]="credentialsFormGroup" [optional]="true"> | 132 | <mat-step [stepControl]="credentialsFormGroup" [optional]="true"> |
125 | <ng-template matStepLabel>{{ 'device.credentials' | translate }}</ng-template> | 133 | <ng-template matStepLabel>{{ 'device.credentials' | translate }}</ng-template> |
126 | <form [formGroup]="credentialsFormGroup" style="padding-bottom: 16px;"> | 134 | <form [formGroup]="credentialsFormGroup" style="padding-bottom: 16px;"> |
@@ -25,7 +25,7 @@ import { | @@ -25,7 +25,7 @@ import { | ||
25 | createDeviceProfileConfiguration, | 25 | createDeviceProfileConfiguration, |
26 | createDeviceProfileTransportConfiguration, | 26 | createDeviceProfileTransportConfiguration, |
27 | DeviceProfile, | 27 | DeviceProfile, |
28 | - DeviceProfileType, | 28 | + DeviceProfileType, DeviceProvisionConfiguration, DeviceProvisionType, |
29 | DeviceTransportType, deviceTransportTypeConfigurationInfoMap, deviceTransportTypeHintMap, | 29 | DeviceTransportType, deviceTransportTypeConfigurationInfoMap, deviceTransportTypeHintMap, |
30 | deviceTransportTypeTranslationMap | 30 | deviceTransportTypeTranslationMap |
31 | } from '@shared/models/device.models'; | 31 | } from '@shared/models/device.models'; |
@@ -75,6 +75,8 @@ export class DeviceWizardDialogComponent extends | @@ -75,6 +75,8 @@ export class DeviceWizardDialogComponent extends | ||
75 | 75 | ||
76 | alarmRulesFormGroup: FormGroup; | 76 | alarmRulesFormGroup: FormGroup; |
77 | 77 | ||
78 | + provisionConfigFormGroup: FormGroup; | ||
79 | + | ||
78 | credentialsFormGroup: FormGroup; | 80 | credentialsFormGroup: FormGroup; |
79 | 81 | ||
80 | customerFormGroup: FormGroup; | 82 | customerFormGroup: FormGroup; |
@@ -142,6 +144,14 @@ export class DeviceWizardDialogComponent extends | @@ -142,6 +144,14 @@ export class DeviceWizardDialogComponent extends | ||
142 | } | 144 | } |
143 | ); | 145 | ); |
144 | 146 | ||
147 | + this.provisionConfigFormGroup = this.fb.group( | ||
148 | + { | ||
149 | + provisionConfiguration: [{ | ||
150 | + type: DeviceProvisionType.DISABLED | ||
151 | + } as DeviceProvisionConfiguration, [Validators.required]] | ||
152 | + } | ||
153 | + ); | ||
154 | + | ||
145 | this.credentialsFormGroup = this.fb.group({ | 155 | this.credentialsFormGroup = this.fb.group({ |
146 | setCredential: [false], | 156 | setCredential: [false], |
147 | credential: [{value: null, disabled: true}] | 157 | credential: [{value: null, disabled: true}] |
@@ -201,7 +211,7 @@ export class DeviceWizardDialogComponent extends | @@ -201,7 +211,7 @@ export class DeviceWizardDialogComponent extends | ||
201 | getFormLabel(index: number): string { | 211 | getFormLabel(index: number): string { |
202 | if (index > 0) { | 212 | if (index > 0) { |
203 | if (!this.createProfile) { | 213 | if (!this.createProfile) { |
204 | - index += 2; | 214 | + index += 3; |
205 | } else if (!this.createTransportConfiguration) { | 215 | } else if (!this.createTransportConfiguration) { |
206 | index += 1; | 216 | index += 1; |
207 | } | 217 | } |
@@ -214,8 +224,10 @@ export class DeviceWizardDialogComponent extends | @@ -214,8 +224,10 @@ export class DeviceWizardDialogComponent extends | ||
214 | case 2: | 224 | case 2: |
215 | return 'device-profile.alarm-rules'; | 225 | return 'device-profile.alarm-rules'; |
216 | case 3: | 226 | case 3: |
217 | - return 'device.credentials'; | 227 | + return 'device-profile.device-provisioning'; |
218 | case 4: | 228 | case 4: |
229 | + return 'device.credentials'; | ||
230 | + case 5: | ||
219 | return 'customer.customer'; | 231 | return 'customer.customer'; |
220 | } | 232 | } |
221 | } | 233 | } |
@@ -246,14 +258,20 @@ export class DeviceWizardDialogComponent extends | @@ -246,14 +258,20 @@ export class DeviceWizardDialogComponent extends | ||
246 | 258 | ||
247 | private createDeviceProfile(): Observable<EntityId> { | 259 | private createDeviceProfile(): Observable<EntityId> { |
248 | if (this.deviceWizardFormGroup.get('addProfileType').value) { | 260 | if (this.deviceWizardFormGroup.get('addProfileType').value) { |
261 | + const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value; | ||
262 | + const provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey; | ||
263 | + delete deviceProvisionConfiguration.provisionDeviceKey; | ||
249 | const deviceProfile: DeviceProfile = { | 264 | const deviceProfile: DeviceProfile = { |
250 | name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value, | 265 | name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value, |
251 | type: DeviceProfileType.DEFAULT, | 266 | type: DeviceProfileType.DEFAULT, |
252 | transportType: this.deviceWizardFormGroup.get('transportType').value, | 267 | transportType: this.deviceWizardFormGroup.get('transportType').value, |
268 | + provisionType: deviceProvisionConfiguration.type, | ||
269 | + provisionDeviceKey, | ||
253 | profileData: { | 270 | profileData: { |
254 | configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), | 271 | configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), |
255 | transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, | 272 | transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, |
256 | - alarms: this.alarmRulesFormGroup.get('alarms').value | 273 | + alarms: this.alarmRulesFormGroup.get('alarms').value, |
274 | + provisionConfiguration: deviceProvisionConfiguration | ||
257 | } | 275 | } |
258 | }; | 276 | }; |
259 | return this.deviceProfileService.saveDeviceProfile(deviceProfile).pipe( | 277 | return this.deviceProfileService.saveDeviceProfile(deviceProfile).pipe( |
@@ -50,6 +50,15 @@ | @@ -50,6 +50,15 @@ | ||
50 | </div> | 50 | </div> |
51 | </div> | 51 | </div> |
52 | </mat-tab> | 52 | </mat-tab> |
53 | +<mat-tab *ngIf="entity" | ||
54 | + label="{{ 'device-profile.device-provisioning' | translate }}" #deviceProvisioning="matTab"> | ||
55 | + <div class="mat-padding" [formGroup]="detailsForm"> | ||
56 | + <div formGroupName="profileData"> | ||
57 | + <tb-device-profile-provision-configuration formControlName="provisionConfiguration"> | ||
58 | + </tb-device-profile-provision-configuration> | ||
59 | + </div> | ||
60 | + </div> | ||
61 | +</mat-tab> | ||
53 | <mat-tab *ngIf="false" | 62 | <mat-tab *ngIf="false" |
54 | label="{{'device-profile.profile-configuration' | translate }}" #deviceProfile="matTab"> | 63 | label="{{'device-profile.profile-configuration' | translate }}" #deviceProfile="matTab"> |
55 | <div class="mat-padding" [formGroup]="detailsForm"> | 64 | <div class="mat-padding" [formGroup]="detailsForm"> |
@@ -43,6 +43,12 @@ export enum MqttTransportPayloadType { | @@ -43,6 +43,12 @@ export enum MqttTransportPayloadType { | ||
43 | PROTOBUF = 'PROTOBUF' | 43 | PROTOBUF = 'PROTOBUF' |
44 | } | 44 | } |
45 | 45 | ||
46 | +export enum DeviceProvisionType { | ||
47 | + DISABLED = 'DISABLED', | ||
48 | + ALLOW_CREATE_NEW_DEVICES = 'ALLOW_CREATE_NEW_DEVICES', | ||
49 | + CHECK_PRE_PROVISIONED_DEVICES = 'CHECK_PRE_PROVISIONED_DEVICES' | ||
50 | +} | ||
51 | + | ||
46 | export interface DeviceConfigurationFormInfo { | 52 | export interface DeviceConfigurationFormInfo { |
47 | hasProfileConfiguration: boolean; | 53 | hasProfileConfiguration: boolean; |
48 | hasDeviceConfiguration: boolean; | 54 | hasDeviceConfiguration: boolean; |
@@ -74,6 +80,15 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st | @@ -74,6 +80,15 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st | ||
74 | ] | 80 | ] |
75 | ); | 81 | ); |
76 | 82 | ||
83 | + | ||
84 | +export const deviceProvisionTypeTranslationMap = new Map<DeviceProvisionType, string>( | ||
85 | + [ | ||
86 | + [DeviceProvisionType.DISABLED, 'device-profile.provision-strategy-disabled'], | ||
87 | + [DeviceProvisionType.ALLOW_CREATE_NEW_DEVICES, 'device-profile.provision-strategy-created-new'], | ||
88 | + [DeviceProvisionType.CHECK_PRE_PROVISIONED_DEVICES, 'device-profile.provision-strategy-check-pre-provisioned'] | ||
89 | + ] | ||
90 | +) | ||
91 | + | ||
77 | export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>( | 92 | export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>( |
78 | [ | 93 | [ |
79 | [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'], | 94 | [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'], |
@@ -148,6 +163,12 @@ export interface DeviceProfileTransportConfiguration extends DeviceProfileTransp | @@ -148,6 +163,12 @@ export interface DeviceProfileTransportConfiguration extends DeviceProfileTransp | ||
148 | type: DeviceTransportType; | 163 | type: DeviceTransportType; |
149 | } | 164 | } |
150 | 165 | ||
166 | +export interface DeviceProvisionConfiguration { | ||
167 | + type: DeviceProvisionType; | ||
168 | + provisionDeviceSecret?: string; | ||
169 | + provisionDeviceKey?: string; | ||
170 | +} | ||
171 | + | ||
151 | export function createDeviceProfileConfiguration(type: DeviceProfileType): DeviceProfileConfiguration { | 172 | export function createDeviceProfileConfiguration(type: DeviceProfileType): DeviceProfileConfiguration { |
152 | let configuration: DeviceProfileConfiguration = null; | 173 | let configuration: DeviceProfileConfiguration = null; |
153 | if (type) { | 174 | if (type) { |
@@ -295,6 +316,7 @@ export interface DeviceProfileData { | @@ -295,6 +316,7 @@ export interface DeviceProfileData { | ||
295 | configuration: DeviceProfileConfiguration; | 316 | configuration: DeviceProfileConfiguration; |
296 | transportConfiguration: DeviceProfileTransportConfiguration; | 317 | transportConfiguration: DeviceProfileTransportConfiguration; |
297 | alarms?: Array<DeviceProfileAlarm>; | 318 | alarms?: Array<DeviceProfileAlarm>; |
319 | + provisionConfiguration?: DeviceProvisionConfiguration; | ||
298 | } | 320 | } |
299 | 321 | ||
300 | export interface DeviceProfile extends BaseData<DeviceProfileId> { | 322 | export interface DeviceProfile extends BaseData<DeviceProfileId> { |
@@ -304,6 +326,8 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { | @@ -304,6 +326,8 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { | ||
304 | default?: boolean; | 326 | default?: boolean; |
305 | type: DeviceProfileType; | 327 | type: DeviceProfileType; |
306 | transportType: DeviceTransportType; | 328 | transportType: DeviceTransportType; |
329 | + provisionType: DeviceProvisionType; | ||
330 | + provisionDeviceKey?: string; | ||
307 | defaultRuleChainId?: RuleChainId; | 331 | defaultRuleChainId?: RuleChainId; |
308 | profileData: DeviceProfileData; | 332 | profileData: DeviceProfileData; |
309 | } | 333 | } |
@@ -930,6 +930,16 @@ | @@ -930,6 +930,16 @@ | ||
930 | "alarm-rule-condition": "Alarm rule condition", | 930 | "alarm-rule-condition": "Alarm rule condition", |
931 | "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", | 931 | "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", |
932 | "edit-alarm-rule-condition": "Edit alarm rule condition", | 932 | "edit-alarm-rule-condition": "Edit alarm rule condition", |
933 | + "device-provisioning": "Device provisioning", | ||
934 | + "provision-strategy": "Provision strategy", | ||
935 | + "provision-strategy-required": "Provision strategy is required.", | ||
936 | + "provision-strategy-disabled": "Disabled", | ||
937 | + "provision-strategy-created-new": "Allow create new devices", | ||
938 | + "provision-strategy-check-pre-provisioned": "Check pre provisioned devices", | ||
939 | + "provision-device-key": "Provision device key", | ||
940 | + "provision-device-key-required": "Provision device key is required.", | ||
941 | + "provision-device-secret": "Provision device secret", | ||
942 | + "provision-device-secret-required": "Provision device secret is required.", | ||
933 | "condition": "Condition", | 943 | "condition": "Condition", |
934 | "condition-type": "Condition type", | 944 | "condition-type": "Condition type", |
935 | "condition-type-simple": "Simple", | 945 | "condition-type-simple": "Simple", |