Commit a3326b4464705b125fab923040d8564c98777b3c

Authored by Andrii Shvaika
1 parent 6e492c78

New type of credentials: MQTT

@@ -23,12 +23,14 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -23,12 +23,14 @@ 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;
26 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
28 import org.thingsboard.server.common.data.DataConstants; 29 import org.thingsboard.server.common.data.DataConstants;
29 import org.thingsboard.server.common.data.Device; 30 import org.thingsboard.server.common.data.Device;
30 import org.thingsboard.server.common.data.DeviceProfile; 31 import org.thingsboard.server.common.data.DeviceProfile;
31 import org.thingsboard.server.common.data.TenantProfile; 32 import org.thingsboard.server.common.data.TenantProfile;
  33 +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
32 import org.thingsboard.server.common.data.id.CustomerId; 34 import org.thingsboard.server.common.data.id.CustomerId;
33 import org.thingsboard.server.common.data.id.DeviceId; 35 import org.thingsboard.server.common.data.id.DeviceId;
34 import org.thingsboard.server.common.data.id.DeviceProfileId; 36 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.relation.EntityRelation; 38 import org.thingsboard.server.common.data.relation.EntityRelation;
37 import org.thingsboard.server.common.data.security.DeviceCredentials; 39 import org.thingsboard.server.common.data.security.DeviceCredentials;
38 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 40 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  41 +import org.thingsboard.server.common.msg.EncryptionUtil;
39 import org.thingsboard.server.common.msg.TbMsg; 42 import org.thingsboard.server.common.msg.TbMsg;
40 import org.thingsboard.server.common.msg.TbMsgDataType; 43 import org.thingsboard.server.common.msg.TbMsgDataType;
41 import org.thingsboard.server.common.msg.TbMsgMetaData; 44 import org.thingsboard.server.common.msg.TbMsgMetaData;
@@ -46,6 +49,7 @@ import org.thingsboard.server.dao.device.DeviceService; @@ -46,6 +49,7 @@ import org.thingsboard.server.dao.device.DeviceService;
46 import org.thingsboard.server.dao.relation.RelationService; 49 import org.thingsboard.server.dao.relation.RelationService;
47 import org.thingsboard.server.dao.tenant.TenantProfileService; 50 import org.thingsboard.server.dao.tenant.TenantProfileService;
48 import org.thingsboard.server.dao.tenant.TenantService; 51 import org.thingsboard.server.dao.tenant.TenantService;
  52 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
49 import org.thingsboard.server.gen.transport.TransportProtos; 53 import org.thingsboard.server.gen.transport.TransportProtos;
50 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; 54 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
51 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
@@ -91,6 +95,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -91,6 +95,7 @@ public class DefaultTransportApiService implements TransportApiService {
91 private final TbClusterService tbClusterService; 95 private final TbClusterService tbClusterService;
92 private final DataDecodingEncodingService dataDecodingEncodingService; 96 private final DataDecodingEncodingService dataDecodingEncodingService;
93 97
  98 +
94 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); 99 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
95 100
96 public DefaultTransportApiService(DeviceProfileService deviceProfileService, TenantService tenantService, 101 public DefaultTransportApiService(DeviceProfileService deviceProfileService, TenantService tenantService,
@@ -117,6 +122,10 @@ public class DefaultTransportApiService implements TransportApiService { @@ -117,6 +122,10 @@ public class DefaultTransportApiService implements TransportApiService {
117 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); 122 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
118 return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN), 123 return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN),
119 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 124 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  125 + } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
  126 + TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg();
  127 + return Futures.transform(validateCredentials(msg),
  128 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
120 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { 129 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
121 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); 130 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
122 return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE), 131 return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE),
@@ -130,7 +139,6 @@ public class DefaultTransportApiService implements TransportApiService { @@ -130,7 +139,6 @@ public class DefaultTransportApiService implements TransportApiService {
130 } else if (transportApiRequestMsg.hasGetDeviceProfileRequestMsg()) { 139 } else if (transportApiRequestMsg.hasGetDeviceProfileRequestMsg()) {
131 return Futures.transform(handle(transportApiRequestMsg.getGetDeviceProfileRequestMsg()), 140 return Futures.transform(handle(transportApiRequestMsg.getGetDeviceProfileRequestMsg()),
132 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 141 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
133 -  
134 } 142 }
135 return Futures.transform(getEmptyTransportApiResponseFuture(), 143 return Futures.transform(getEmptyTransportApiResponseFuture(),
136 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 144 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
@@ -146,6 +154,62 @@ public class DefaultTransportApiService implements TransportApiService { @@ -146,6 +154,62 @@ public class DefaultTransportApiService implements TransportApiService {
146 } 154 }
147 } 155 }
148 156
  157 + private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) {
  158 + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());
  159 + if (credentials != null) {
  160 + if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
  161 + return getDeviceInfo(credentials.getDeviceId(), credentials);
  162 + } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {
  163 + if (!checkMqttCredentials(mqtt, credentials)) {
  164 + credentials = null;
  165 + }
  166 + }
  167 + }
  168 + if (credentials == null) {
  169 + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName()));
  170 + if (credentials == null) {
  171 + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId()));
  172 + }
  173 + }
  174 + if (credentials != null) {
  175 + return getDeviceInfo(credentials.getDeviceId(), credentials);
  176 + } else {
  177 + return getEmptyTransportApiResponseFuture();
  178 + }
  179 + }
  180 +
  181 + private DeviceCredentials checkMqttCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg clientCred, String credId) {
  182 + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credId);
  183 + if (deviceCredentials != null && deviceCredentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {
  184 + if (!checkMqttCredentials(clientCred, deviceCredentials)) {
  185 + return null;
  186 + } else {
  187 + return deviceCredentials;
  188 + }
  189 + }
  190 + return null;
  191 + }
  192 +
  193 + private boolean checkMqttCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg clientCred, DeviceCredentials deviceCredentials) {
  194 + BasicMqttCredentials dbCred = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), BasicMqttCredentials.class);
  195 + if (!StringUtils.isEmpty(dbCred.getClientId()) && !dbCred.getClientId().equals(clientCred.getClientId())) {
  196 + return false;
  197 + }
  198 + if (!StringUtils.isEmpty(dbCred.getUserName()) && !dbCred.getUserName().equals(clientCred.getUserName())) {
  199 + return false;
  200 + }
  201 + if (!StringUtils.isEmpty(dbCred.getPassword())) {
  202 + if (StringUtils.isEmpty(clientCred.getPassword())) {
  203 + return false;
  204 + } else {
  205 + if (!dbCred.getPassword().equals(clientCred.getPassword())) {
  206 + return false;
  207 + }
  208 + }
  209 + }
  210 + return true;
  211 + }
  212 +
149 private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { 213 private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) {
150 DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); 214 DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
151 ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); 215 ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId);
@@ -237,7 +301,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -237,7 +301,7 @@ public class DefaultTransportApiService implements TransportApiService {
237 builder.setCredentialsBody(credentials.getCredentialsValue()); 301 builder.setCredentialsBody(credentials.getCredentialsValue());
238 } 302 }
239 return TransportApiResponseMsg.newBuilder() 303 return TransportApiResponseMsg.newBuilder()
240 - .setValidateTokenResponseMsg(builder.build()).build(); 304 + .setValidateCredResponseMsg(builder.build()).build();
241 } catch (JsonProcessingException e) { 305 } catch (JsonProcessingException e) {
242 log.warn("[{}] Failed to lookup device by id", deviceId, e); 306 log.warn("[{}] Failed to lookup device by id", deviceId, e);
243 return getEmptyTransportApiResponse(); 307 return getEmptyTransportApiResponse();
@@ -265,6 +329,6 @@ public class DefaultTransportApiService implements TransportApiService { @@ -265,6 +329,6 @@ public class DefaultTransportApiService implements TransportApiService {
265 329
266 private TransportApiResponseMsg getEmptyTransportApiResponse() { 330 private TransportApiResponseMsg getEmptyTransportApiResponse() {
267 return TransportApiResponseMsg.newBuilder() 331 return TransportApiResponseMsg.newBuilder()
268 - .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build(); 332 + .setValidateCredResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build();
269 } 333 }
270 } 334 }
  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 BasicMqttCredentials {
  22 +
  23 + private String clientId;
  24 + private String userName;
  25 + private String password;
  26 +
  27 +}
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.msg; @@ -18,6 +18,7 @@ package org.thingsboard.server.common.msg;
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.bouncycastle.crypto.digests.SHA3Digest; 19 import org.bouncycastle.crypto.digests.SHA3Digest;
20 import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 20 import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
  21 +
21 /** 22 /**
22 * @author Valerii Sosliuk 23 * @author Valerii Sosliuk
23 */ 24 */
@@ -30,8 +31,8 @@ public class EncryptionUtil { @@ -30,8 +31,8 @@ public class EncryptionUtil {
30 public static String trimNewLines(String input) { 31 public static String trimNewLines(String input) {
31 return input.replaceAll("-----BEGIN CERTIFICATE-----", "") 32 return input.replaceAll("-----BEGIN CERTIFICATE-----", "")
32 .replaceAll("-----END CERTIFICATE-----", "") 33 .replaceAll("-----END CERTIFICATE-----", "")
33 - .replaceAll("\n","")  
34 - .replaceAll("\r",""); 34 + .replaceAll("\n", "")
  35 + .replaceAll("\r", "");
35 } 36 }
36 37
37 public static String getSha3Hash(String data) { 38 public static String getSha3Hash(String data) {
@@ -45,4 +46,20 @@ public class EncryptionUtil { @@ -45,4 +46,20 @@ public class EncryptionUtil {
45 String sha3Hash = ByteUtils.toHexString(hashedBytes); 46 String sha3Hash = ByteUtils.toHexString(hashedBytes);
46 return sha3Hash; 47 return sha3Hash;
47 } 48 }
  49 +
  50 + public static String getSha3Hash(String delim, String... tokens) {
  51 + StringBuilder sb = new StringBuilder();
  52 + boolean first = true;
  53 + for (String token : tokens) {
  54 + if (token != null && !token.isEmpty()) {
  55 + if (first) {
  56 + first = false;
  57 + } else {
  58 + sb.append(delim);
  59 + }
  60 + sb.append(token);
  61 + }
  62 + }
  63 + return getSha3Hash(sb.toString());
  64 + }
48 } 65 }
@@ -147,6 +147,12 @@ message ValidateDeviceX509CertRequestMsg { @@ -147,6 +147,12 @@ message ValidateDeviceX509CertRequestMsg {
147 string hash = 1; 147 string hash = 1;
148 } 148 }
149 149
  150 +message ValidateBasicMqttCredRequestMsg {
  151 + string clientId = 1;
  152 + string userName = 2;
  153 + string password = 3;
  154 +}
  155 +
150 message ValidateDeviceCredentialsResponseMsg { 156 message ValidateDeviceCredentialsResponseMsg {
151 DeviceInfoProto deviceInfo = 1; 157 DeviceInfoProto deviceInfo = 1;
152 string credentialsBody = 2; 158 string credentialsBody = 2;
@@ -429,11 +435,12 @@ message TransportApiRequestMsg { @@ -429,11 +435,12 @@ message TransportApiRequestMsg {
429 GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3; 435 GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3;
430 GetTenantRoutingInfoRequestMsg getTenantRoutingInfoRequestMsg = 4; 436 GetTenantRoutingInfoRequestMsg getTenantRoutingInfoRequestMsg = 4;
431 GetDeviceProfileRequestMsg getDeviceProfileRequestMsg = 5; 437 GetDeviceProfileRequestMsg getDeviceProfileRequestMsg = 5;
  438 + ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6;
432 } 439 }
433 440
434 /* Response from ThingsBoard Core Service to Transport Service */ 441 /* Response from ThingsBoard Core Service to Transport Service */
435 message TransportApiResponseMsg { 442 message TransportApiResponseMsg {
436 - ValidateDeviceCredentialsResponseMsg validateTokenResponseMsg = 1; 443 + ValidateDeviceCredentialsResponseMsg validateCredResponseMsg = 1;
437 GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; 444 GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
438 GetTenantRoutingInfoResponseMsg getTenantRoutingInfoResponseMsg = 4; 445 GetTenantRoutingInfoResponseMsg getTenantRoutingInfoResponseMsg = 4;
439 GetDeviceProfileResponseMsg getDeviceProfileResponseMsg = 5; 446 GetDeviceProfileResponseMsg getDeviceProfileResponseMsg = 5;
@@ -66,6 +66,7 @@ import javax.net.ssl.SSLPeerUnverifiedException; @@ -66,6 +66,7 @@ import javax.net.ssl.SSLPeerUnverifiedException;
66 import javax.security.cert.X509Certificate; 66 import javax.security.cert.X509Certificate;
67 import java.io.IOException; 67 import java.io.IOException;
68 import java.net.InetSocketAddress; 68 import java.net.InetSocketAddress;
  69 +import java.nio.charset.StandardCharsets;
69 import java.util.ArrayList; 70 import java.util.ArrayList;
70 import java.util.List; 71 import java.util.List;
71 import java.util.UUID; 72 import java.util.UUID;
@@ -365,25 +366,27 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -365,25 +366,27 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
365 private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { 366 private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
366 String userName = msg.payload().userName(); 367 String userName = msg.payload().userName();
367 log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName); 368 log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName);
368 - if (StringUtils.isEmpty(userName)) {  
369 - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD));  
370 - ctx.close();  
371 - } else {  
372 - transportService.process(DeviceTransportType.MQTT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(userName).build(),  
373 - new TransportServiceCallback<ValidateDeviceCredentialsResponse>() {  
374 - @Override  
375 - public void onSuccess(ValidateDeviceCredentialsResponse msg) {  
376 - onValidateDeviceResponse(msg, ctx);  
377 - }  
378 -  
379 - @Override  
380 - public void onError(Throwable e) {  
381 - log.trace("[{}] Failed to process credentials: {}", address, userName, e);  
382 - ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE));  
383 - ctx.close();  
384 - }  
385 - }); 369 + TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
  370 + .setClientId(msg.payload().clientIdentifier())
  371 + .setUserName(userName);
  372 + String password = msg.payload().password();
  373 + if (password != null) {
  374 + request.setPassword(password);
386 } 375 }
  376 + transportService.process(DeviceTransportType.MQTT, request.build(),
  377 + new TransportServiceCallback<ValidateDeviceCredentialsResponse>() {
  378 + @Override
  379 + public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  380 + onValidateDeviceResponse(msg, ctx);
  381 + }
  382 +
  383 + @Override
  384 + public void onError(Throwable e) {
  385 + log.trace("[{}] Failed to process credentials: {}", address, userName, e);
  386 + ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE));
  387 + ctx.close();
  388 + }
  389 + });
387 } 390 }
388 391
389 private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) { 392 private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) {
@@ -23,7 +23,6 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes @@ -23,7 +23,6 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
23 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 23 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
24 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
26 -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;  
27 import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoRequestMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoRequestMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoResponseMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.GetTenantRoutingInfoResponseMsg;
29 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
@@ -35,7 +34,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; @@ -35,7 +34,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
35 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; 34 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
36 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; 35 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
37 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; 36 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
38 -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; 37 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg;
39 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; 38 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
40 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; 39 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
41 40
@@ -49,6 +48,9 @@ public interface TransportService { @@ -49,6 +48,9 @@ public interface TransportService {
49 void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg, 48 void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg,
50 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback); 49 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
51 50
  51 + void process(DeviceTransportType transportType, ValidateBasicMqttCredRequestMsg msg,
  52 + TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
  53 +
52 void process(DeviceTransportType transportType, ValidateDeviceX509CertRequestMsg msg, 54 void process(DeviceTransportType transportType, ValidateDeviceX509CertRequestMsg msg,
53 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback); 55 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
54 56
@@ -252,9 +252,20 @@ public class DefaultTransportService implements TransportService { @@ -252,9 +252,20 @@ public class DefaultTransportService implements TransportService {
252 } 252 }
253 253
254 @Override 254 @Override
255 - public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceTokenRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) { 255 + public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceTokenRequestMsg msg,
  256 + TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
256 log.trace("Processing msg: {}", msg); 257 log.trace("Processing msg: {}", msg);
257 - TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()); 258 + TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(),
  259 + TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build());
  260 + doProcess(transportType, protoMsg, callback);
  261 + }
  262 +
  263 + @Override
  264 + public void process(DeviceTransportType transportType, TransportProtos.ValidateBasicMqttCredRequestMsg msg,
  265 + TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
  266 + log.trace("Processing msg: {}", msg);
  267 + TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(),
  268 + TransportApiRequestMsg.newBuilder().setValidateBasicMqttCredRequestMsg(msg).build());
258 doProcess(transportType, protoMsg, callback); 269 doProcess(transportType, protoMsg, callback);
259 } 270 }
260 271
@@ -265,9 +276,10 @@ public class DefaultTransportService implements TransportService { @@ -265,9 +276,10 @@ public class DefaultTransportService implements TransportService {
265 doProcess(transportType, protoMsg, callback); 276 doProcess(transportType, protoMsg, callback);
266 } 277 }
267 278
268 - private void doProcess(DeviceTransportType transportType, TbProtoQueueMsg<TransportApiRequestMsg> protoMsg, TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) { 279 + private void doProcess(DeviceTransportType transportType, TbProtoQueueMsg<TransportApiRequestMsg> protoMsg,
  280 + TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
269 ListenableFuture<ValidateDeviceCredentialsResponse> response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> { 281 ListenableFuture<ValidateDeviceCredentialsResponse> response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> {
270 - TransportProtos.ValidateDeviceCredentialsResponseMsg msg = tmp.getValue().getValidateTokenResponseMsg(); 282 + TransportProtos.ValidateDeviceCredentialsResponseMsg msg = tmp.getValue().getValidateCredResponseMsg();
271 ValidateDeviceCredentialsResponse.ValidateDeviceCredentialsResponseBuilder result = ValidateDeviceCredentialsResponse.builder(); 283 ValidateDeviceCredentialsResponse.ValidateDeviceCredentialsResponseBuilder result = ValidateDeviceCredentialsResponse.builder();
272 if (msg.hasDeviceInfo()) { 284 if (msg.hasDeviceInfo()) {
273 result.credentials(msg.getCredentialsBody()); 285 result.credentials(msg.getCredentialsBody());
@@ -21,18 +21,20 @@ import org.hibernate.exception.ConstraintViolationException; @@ -21,18 +21,20 @@ import org.hibernate.exception.ConstraintViolationException;
21 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.cache.annotation.CacheEvict; 22 import org.springframework.cache.annotation.CacheEvict;
23 import org.springframework.cache.annotation.Cacheable; 23 import org.springframework.cache.annotation.Cacheable;
  24 +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
24 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
25 import org.springframework.util.StringUtils; 26 import org.springframework.util.StringUtils;
26 import org.thingsboard.server.common.data.Device; 27 import org.thingsboard.server.common.data.Device;
  28 +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
27 import org.thingsboard.server.common.data.id.DeviceId; 29 import org.thingsboard.server.common.data.id.DeviceId;
28 import org.thingsboard.server.common.data.id.EntityId; 30 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.TenantId; 31 import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.security.DeviceCredentials; 32 import org.thingsboard.server.common.data.security.DeviceCredentials;
31 -import org.thingsboard.server.common.data.security.DeviceCredentialsType;  
32 import org.thingsboard.server.common.msg.EncryptionUtil; 33 import org.thingsboard.server.common.msg.EncryptionUtil;
33 import org.thingsboard.server.dao.entity.AbstractEntityService; 34 import org.thingsboard.server.dao.entity.AbstractEntityService;
34 import org.thingsboard.server.dao.exception.DataValidationException; 35 import org.thingsboard.server.dao.exception.DataValidationException;
35 import org.thingsboard.server.dao.service.DataValidator; 36 import org.thingsboard.server.dao.service.DataValidator;
  37 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
36 38
37 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; 39 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE;
38 import static org.thingsboard.server.dao.service.Validator.validateId; 40 import static org.thingsboard.server.dao.service.Validator.validateId;
@@ -75,8 +77,16 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen @@ -75,8 +77,16 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
75 } 77 }
76 78
77 private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { 79 private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
78 - if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {  
79 - formatCertData(deviceCredentials); 80 + if(deviceCredentials.getCredentialsType() == null){
  81 + throw new DataValidationException("Device credentials type should be specified");
  82 + }
  83 + switch (deviceCredentials.getCredentialsType()) {
  84 + case X509_CERTIFICATE:
  85 + formatCertData(deviceCredentials);
  86 + break;
  87 + case MQTT_BASIC:
  88 + formatSimpleMqttCredentials(deviceCredentials);
  89 + break;
80 } 90 }
81 log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); 91 log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
82 credentialsValidator.validate(deviceCredentials, id -> tenantId); 92 credentialsValidator.validate(deviceCredentials, id -> tenantId);
@@ -93,6 +103,32 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen @@ -93,6 +103,32 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
93 } 103 }
94 } 104 }
95 105
  106 + private void formatSimpleMqttCredentials(DeviceCredentials deviceCredentials) {
  107 + BasicMqttCredentials mqttCredentials;
  108 + try {
  109 + mqttCredentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), BasicMqttCredentials.class);
  110 + if (mqttCredentials == null) {
  111 + throw new IllegalArgumentException();
  112 + }
  113 + } catch (IllegalArgumentException e) {
  114 + throw new DataValidationException("Invalid credentials body for simple mqtt credentials!");
  115 + }
  116 + if (StringUtils.isEmpty(mqttCredentials.getClientId()) && StringUtils.isEmpty(mqttCredentials.getUserName())) {
  117 + throw new DataValidationException("Both mqtt client id and user name are empty!");
  118 + }
  119 + if (StringUtils.isEmpty(mqttCredentials.getClientId())) {
  120 + deviceCredentials.setCredentialsId(mqttCredentials.getUserName());
  121 + } else if (StringUtils.isEmpty(mqttCredentials.getUserName())) {
  122 + deviceCredentials.setCredentialsId(EncryptionUtil.getSha3Hash(mqttCredentials.getClientId()));
  123 + } else {
  124 + deviceCredentials.setCredentialsId(EncryptionUtil.getSha3Hash("|", mqttCredentials.getClientId(), mqttCredentials.getUserName()));
  125 + }
  126 + if (!StringUtils.isEmpty(mqttCredentials.getPassword())) {
  127 + mqttCredentials.setPassword(mqttCredentials.getPassword());
  128 + }
  129 + deviceCredentials.setCredentialsValue(JacksonUtil.toString(mqttCredentials));
  130 + }
  131 +
96 private void formatCertData(DeviceCredentials deviceCredentials) { 132 private void formatCertData(DeviceCredentials deviceCredentials) {
97 String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); 133 String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue());
98 String sha3Hash = EncryptionUtil.getSha3Hash(cert); 134 String sha3Hash = EncryptionUtil.getSha3Hash(cert);