Commit 15be6d60a314d2dbaf9ad09dd4ed05a55349d364

Authored by zbeacon
1 parent d2282080

Added strategy checking

... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.service.device;
17 17
18   -import com.datastax.oss.driver.api.core.uuid.Uuids;
19 18 import com.fasterxml.jackson.core.JsonProcessingException;
20 19 import com.fasterxml.jackson.databind.node.ObjectNode;
21 20 import com.google.common.util.concurrent.Futures;
... ... @@ -30,9 +29,11 @@ import org.thingsboard.server.common.data.Device;
30 29 import org.thingsboard.server.common.data.DeviceProfile;
31 30 import org.thingsboard.server.common.data.DeviceProfileType;
32 31 import org.thingsboard.server.common.data.audit.ActionType;
  32 +import org.thingsboard.server.common.data.device.data.ProvisionDeviceConfiguration;
33 33 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.ProvisionRequestValidationStrategyType;
  35 +import org.thingsboard.server.common.data.id.CustomerId;
34 36 import org.thingsboard.server.common.data.id.TenantId;
35   -import org.thingsboard.server.common.data.id.UserId;
36 37 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
37 38 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
38 39 import org.thingsboard.server.common.data.kv.StringDataEntry;
... ... @@ -45,6 +46,7 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
45 46 import org.thingsboard.server.dao.attributes.AttributesService;
46 47 import org.thingsboard.server.dao.audit.AuditLogService;
47 48 import org.thingsboard.server.dao.device.DeviceCredentialsService;
  49 +import org.thingsboard.server.dao.device.DeviceDao;
48 50 import org.thingsboard.server.dao.device.DeviceProfileDao;
49 51 import org.thingsboard.server.dao.device.DeviceProvisionService;
50 52 import org.thingsboard.server.dao.device.DeviceService;
... ... @@ -65,8 +67,6 @@ import java.util.List;
65 67 import java.util.Optional;
66 68 import java.util.concurrent.locks.ReentrantLock;
67 69
68   -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
69   -
70 70
71 71 @Service
72 72 @Slf4j
... ... @@ -77,11 +77,12 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
77 77 private static final String DEVICE_PROVISION_STATE = "provisionState";
78 78 private static final String PROVISIONED_STATE = "provisioned";
79 79
80   - private static final UserId PROVISION_USER_ID = UserId.fromString(NULL_UUID.toString());
81   -
82 80 private final ReentrantLock deviceCreationLock = new ReentrantLock();
83 81
84 82 @Autowired
  83 + DeviceDao deviceDao;
  84 +
  85 + @Autowired
85 86 DeviceProfileDao deviceProfileDao;
86 87
87 88 @Autowired
... ... @@ -105,39 +106,52 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
105 106
106 107 @Override
107 108 public ListenableFuture<ProvisionResponse> provisionDevice(ProvisionRequest provisionRequest) {
108   - DeviceProfile targetProfile = deviceProfileDao.findProfileByTenantIdAndProfileDataProvisionConfigurationPair(
  109 + Device targetDevice = deviceDao.findDeviceByTenantIdAndDeviceDataProvisionConfigurationPair(
109 110 TenantId.SYS_TENANT_ID,
110 111 provisionRequest.getCredentials().getProvisionDeviceKey(),
111 112 provisionRequest.getCredentials().getProvisionDeviceSecret());
112 113
113   - if (targetProfile.getProfileData().getConfiguration().getType() != DeviceProfileType.PROVISION) {
114   - return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND));
115   - }
  114 + if (targetDevice != null) {
  115 + if (targetDevice.getDeviceData().getConfiguration().getType() != DeviceProfileType.PROVISION) {
  116 + return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND));
  117 + }
116 118
117   - ProvisionDeviceProfileConfiguration currentProfileConfiguration = (ProvisionDeviceProfileConfiguration) targetProfile.getProfileData().getConfiguration();
118   - if (!new ProvisionDeviceProfileConfiguration(provisionRequest.getCredentials().getProvisionDeviceKey(), provisionRequest.getCredentials().getProvisionDeviceSecret()).equals(currentProfileConfiguration)) {
119   - return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND));
120   - }
  119 + DeviceProfile targetProfile = deviceProfileDao.findById(TenantId.SYS_TENANT_ID, targetDevice.getDeviceProfileId().getId());
121 120
122   - Device device = deviceService.findDeviceByTenantIdAndName(targetProfile.getTenantId(), provisionRequest.getDeviceName());
123   - switch (currentProfileConfiguration.getStrategy()) {
124   - case CHECK_NEW_DEVICE:
125   - if (device == null) {
126   - return createDevice(provisionRequest, targetProfile);
127   - } else {
128   - log.warn("[{}] The device is present and could not be provisioned once more!", device.getName());
129   - notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false);
  121 + ProvisionDeviceConfiguration currentProfileConfiguration = (ProvisionDeviceConfiguration) targetDevice.getDeviceData().getConfiguration();
  122 + if (!new ProvisionDeviceConfiguration(provisionRequest.getCredentials().getProvisionDeviceKey(), provisionRequest.getCredentials().getProvisionDeviceSecret()).equals(currentProfileConfiguration)) {
  123 + return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND));
  124 + }
  125 + ProvisionRequestValidationStrategyType targetStrategy = getStrategy(targetProfile);
  126 + switch (targetStrategy) {
  127 + case CHECK_NEW_DEVICE:
  128 + log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName());
  129 + notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false);
130 130 return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE));
131   - }
132   - case CHECK_PRE_PROVISIONED_DEVICE:
133   - if (device == null) {
  131 + case CHECK_PRE_PROVISIONED_DEVICE:
  132 + return processProvision(targetDevice, provisionRequest);
  133 + default:
  134 + throw new RuntimeException("Strategy is not supported - " + targetStrategy.name());
  135 + }
  136 + } else {
  137 + DeviceProfile targetProfile = deviceProfileDao.findProfileByTenantIdAndProfileDataProvisionConfigurationPair(
  138 + TenantId.SYS_TENANT_ID,
  139 + provisionRequest.getCredentials().getProvisionDeviceKey(),
  140 + provisionRequest.getCredentials().getProvisionDeviceSecret()
  141 + );
  142 + if (targetProfile.getProfileData().getConfiguration().getType() != DeviceProfileType.PROVISION) {
  143 + return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.NOT_FOUND));
  144 + }
  145 + ProvisionRequestValidationStrategyType targetStrategy = getStrategy(targetProfile);
  146 + switch (targetStrategy) {
  147 + case CHECK_NEW_DEVICE:
  148 + return createDevice(provisionRequest, targetProfile);
  149 + case CHECK_PRE_PROVISIONED_DEVICE:
134 150 log.warn("[{}] Failed to find pre provisioned device!", provisionRequest.getDeviceName());
135 151 return Futures.immediateFuture(new ProvisionResponse(null, ProvisionResponseStatus.FAILURE));
136   - } else {
137   - return processProvision(device, provisionRequest);
138   - }
139   - default:
140   - throw new RuntimeException("Strategy is not supported - " + currentProfileConfiguration.getStrategy().name());
  152 + default:
  153 + throw new RuntimeException("Strategy is not supported - " + targetStrategy.name());
  154 + }
141 155 }
142 156 }
143 157
... ... @@ -178,6 +192,16 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
178 192 }
179 193 }
180 194
  195 + private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) {
  196 + pushProvisionEventToRuleEngine(provisionRequest, device, type);
  197 + logAction(device.getTenantId(), device.getCustomerId(), device, success, provisionRequest);
  198 + }
  199 +
  200 + private ProvisionRequestValidationStrategyType getStrategy(DeviceProfile profile) {
  201 + return ((ProvisionDeviceProfileConfiguration) profile.getProfileData().getConfiguration()).getStrategy();
  202 +
  203 + }
  204 +
181 205 private ListenableFuture<ProvisionResponse> processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) {
182 206 Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName());
183 207 if (device == null) {
... ... @@ -221,15 +245,10 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
221 245 return credentials;
222 246 }
223 247
224   - private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) {
225   - pushProvisionEventToRuleEngine(provisionRequest, device, type);
226   - logAction(device.getTenantId(), device, success, provisionRequest);
227   - }
228   -
229 248 private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) {
230 249 try {
231 250 ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(request);
232   - TbMsg msg = new TbMsg(Uuids.timeBased(), type, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode), null, null, 0L);
  251 + TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
233 252 sendToRuleEngine(device.getTenantId(), msg, null);
234 253 } catch (JsonProcessingException | IllegalArgumentException e) {
235 254 log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e);
... ... @@ -239,7 +258,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
239 258 private void pushDeviceCreatedEventToRuleEngine(Device device) {
240 259 try {
241 260 ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device);
242   - TbMsg msg = new TbMsg(Uuids.timeBased(), DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode), null, null, 0L);
  261 + TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
243 262 sendToRuleEngine(device.getTenantId(), msg, null);
244 263 } catch (JsonProcessingException | IllegalArgumentException e) {
245 264 log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e);
... ... @@ -260,8 +279,8 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
260 279 return metaData;
261 280 }
262 281
263   - private void logAction(TenantId tenantId, Device device, boolean success, ProvisionRequest provisionRequest) {
  282 + private void logAction(TenantId tenantId, CustomerId customerId, Device device, boolean success, ProvisionRequest provisionRequest) {
264 283 ActionType actionType = success ? ActionType.PROVISION_SUCCESS : ActionType.PROVISION_FAILURE;
265   - auditLogService.logEntityAction(tenantId, null, null, device.getName(), device.getId(), device, actionType, null, provisionRequest);
  284 + auditLogService.logEntityAction(tenantId, customerId, null, device.getName(), device.getId(), device, actionType, null, provisionRequest);
266 285 }
267 286 }
... ...
... ... @@ -17,7 +17,7 @@ package org.thingsboard.server.dao.device.provision;
17 17
18 18 import lombok.AllArgsConstructor;
19 19 import lombok.Data;
20   -import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileConfiguration;
  20 +import org.thingsboard.server.common.data.device.data.ProvisionDeviceConfiguration;
21 21
22 22 @Data
23 23 @AllArgsConstructor
... ... @@ -25,5 +25,5 @@ public class ProvisionRequest {
25 25 private String deviceName;
26 26 private String deviceType;
27 27 private String x509CertPubKey;
28   - private ProvisionDeviceProfileConfiguration credentials;
  28 + private ProvisionDeviceConfiguration credentials;
29 29 }
... ...
... ... @@ -72,11 +72,12 @@ public class DataConstants {
72 72 public static final String SECRET_KEY_FIELD_NAME = "secretKey";
73 73 public static final String DURATION_MS_FIELD_NAME = "durationMs";
74 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 +
75 79 public static final String DEVICE_NAME = "deviceName";
76 80 public static final String DEVICE_TYPE = "deviceType";
77 81 public static final String CERT_PUB_KEY = "x509CertPubKey";
78 82
79   - public static final String PROVISION_KEY = "provisionDeviceKey";
80   - public static final String PROVISION_SECRET = "provisionDeviceSecret";
81   -
82 83 }
... ...
  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.data;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import lombok.Data;
  21 +import org.thingsboard.server.common.data.DeviceProfileType;
  22 +import org.thingsboard.server.common.data.device.profile.DeviceProfileConfiguration;
  23 +import org.thingsboard.server.common.data.device.profile.ProvisionRequestValidationStrategyType;
  24 +
  25 +import java.util.Objects;
  26 +
  27 +@Data
  28 +public class ProvisionDeviceConfiguration implements DeviceConfiguration {
  29 +
  30 + private String provisionDeviceKey;
  31 + private String provisionDeviceSecret;
  32 +
  33 + @Override
  34 + public DeviceProfileType getType() {
  35 + return DeviceProfileType.PROVISION;
  36 + }
  37 +
  38 + @JsonCreator
  39 + public ProvisionDeviceConfiguration(@JsonProperty("provisionDeviceKey") String provisionProfileKey, @JsonProperty("provisionDeviceSecret") String provisionProfileSecret) {
  40 + this.provisionDeviceKey = provisionProfileKey;
  41 + this.provisionDeviceSecret = provisionProfileSecret;
  42 + }
  43 +
  44 + @Override
  45 + public boolean equals(Object o) {
  46 + if (this == o) return true;
  47 + if (o == null || getClass() != o.getClass()) return false;
  48 + ProvisionDeviceConfiguration that = (ProvisionDeviceConfiguration) o;
  49 + return provisionDeviceKey.equals(that.provisionDeviceKey) &&
  50 + provisionDeviceSecret.equals(that.provisionDeviceSecret);
  51 + }
  52 +
  53 + @Override
  54 + public int hashCode() {
  55 + return Objects.hash(provisionDeviceKey, provisionDeviceSecret);
  56 + }
  57 +}
... ...
... ... @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
19 19 import com.fasterxml.jackson.annotation.JsonProperty;
20 20 import lombok.Data;
21 21 import org.thingsboard.server.common.data.DeviceProfileType;
  22 +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
  23 +import org.thingsboard.server.common.data.device.data.ProvisionDeviceConfiguration;
22 24
23 25 import java.util.Objects;
24 26
... ...
... ... @@ -34,10 +34,12 @@ import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
34 34 import io.netty.handler.codec.mqtt.MqttTopicSubscription;
35 35 import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage;
36 36 import io.netty.handler.ssl.SslHandler;
  37 +import io.netty.util.CharsetUtil;
37 38 import io.netty.util.ReferenceCountUtil;
38 39 import io.netty.util.concurrent.Future;
39 40 import io.netty.util.concurrent.GenericFutureListener;
40 41 import lombok.extern.slf4j.Slf4j;
  42 +import org.thingsboard.server.common.data.DataConstants;
41 43 import org.thingsboard.server.common.data.DeviceProfile;
42 44 import org.thingsboard.server.common.data.DeviceTransportType;
43 45 import org.thingsboard.server.common.data.device.profile.MqttTopics;
... ... @@ -51,6 +53,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
51 53 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
52 54 import org.thingsboard.server.common.transport.service.DefaultTransportService;
53 55 import org.thingsboard.server.gen.transport.TransportProtos;
  56 +import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
54 57 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
55 58 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
56 59 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
... ... @@ -420,11 +423,19 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
420 423
421 424 private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
422 425 log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier());
423   - X509Certificate cert;
424   - if (sslHandler != null && (cert = getX509Certificate()) != null) {
425   - processX509CertConnect(ctx, cert);
  426 + String userName = msg.payload().userName();
  427 + if (DataConstants.PROVISION.equals(userName)) {
  428 + deviceSessionCtx.setDeviceInfo(new TransportDeviceInfo());
  429 + deviceSessionCtx.setProvisionOnly(true);
  430 + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
426 431 } else {
427   - processAuthTokenConnect(ctx, msg);
  432 + X509Certificate cert;
  433 +
  434 + if (sslHandler != null && (cert = getX509Certificate()) != null) {
  435 + processX509CertConnect(ctx, cert);
  436 + } else {
  437 + processAuthTokenConnect(ctx, msg);
  438 + }
428 439 }
429 440 }
430 441
... ...
... ... @@ -215,4 +215,6 @@ public interface DeviceDao extends Dao<Device> {
215 215 */
216 216 PageData<Device> findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink);
217 217
  218 + Device findDeviceByTenantIdAndDeviceDataProvisionConfigurationPair(TenantId tenantId, String provisionDeviceKey, String provisionDeviceSecret);
  219 +
218 220 }
... ...
... ... @@ -20,8 +20,10 @@ import org.springframework.data.domain.Pageable;
20 20 import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.DeviceProfileInfo;
23 24 import org.thingsboard.server.dao.model.sql.DeviceEntity;
24 25 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
  26 +import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
25 27
26 28 import java.util.List;
27 29 import java.util.UUID;
... ... @@ -169,4 +171,12 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
169 171
170 172 Long countByDeviceProfileId(UUID deviceProfileId);
171 173
  174 + @Query(value = "SELECT d FROM Device d " +
  175 + "WHERE d.tenant_id = :tenantId " +
  176 + "AND d.device_data::jsonb->>('configuration', 'provisionDeviceKey') = :provisionDeviceKey " +
  177 + "AND d.device_data::jsonb->>('configuration', 'provisionDeviceSecret') = :provisionDeviceSecret",
  178 + nativeQuery = true)
  179 + DeviceEntity findDeviceByTenantIdAndDeviceDataProvisionConfigurationPair(@Param("tenantId") UUID tenantId,
  180 + @Param("provisionDeviceKey") String provisionDeviceKey,
  181 + @Param("provisionDeviceSecret") String provisionDeviceSecret);
172 182 }
... ...
... ... @@ -22,6 +22,8 @@ import org.springframework.stereotype.Component;
22 22 import org.springframework.util.StringUtils;
23 23 import org.thingsboard.server.common.data.Device;
24 24 import org.thingsboard.server.common.data.DeviceInfo;
  25 +import org.thingsboard.server.common.data.DeviceProfile;
  26 +import org.thingsboard.server.common.data.DeviceProfileInfo;
25 27 import org.thingsboard.server.common.data.EntitySubtype;
26 28 import org.thingsboard.server.common.data.EntityType;
27 29 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -219,6 +221,11 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
219 221 return deviceRepository.countByDeviceProfileId(deviceProfileId);
220 222 }
221 223
  224 + @Override
  225 + public Device findDeviceByTenantIdAndDeviceDataProvisionConfigurationPair(TenantId tenantId, String provisionDeviceKey, String provisionDeviceSecret) {
  226 + return DaoUtil.getData(deviceRepository.findDeviceByTenantIdAndDeviceDataProvisionConfigurationPair(tenantId.getId(), provisionDeviceKey, provisionDeviceSecret));
  227 + }
  228 +
222 229 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
223 230 List<EntitySubtype> list = Collections.emptyList();
224 231 if (types != null && !types.isEmpty()) {
... ...