Add ability to upgrade device credentials if chain match with uploaded device profile certificate.

This commit is contained in:
Andrii Landiak 2023-01-12 17:18:29 +02:00
parent dccc19a9b6
commit 5d35fa1f26
20 changed files with 388 additions and 73 deletions

View File

@ -65,8 +65,8 @@ import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceProvisionService; import org.thingsboard.server.dao.device.DeviceProvisionService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
@ -90,11 +90,15 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRespon
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.UpdateOrCreateDeviceX509CertRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceProfileCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceProfileX509CertRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
@ -108,6 +112,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH; import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH;
@ -128,6 +134,7 @@ public class DefaultTransportApiService implements TransportApiService {
private final TbTenantProfileCache tenantProfileCache; private final TbTenantProfileCache tenantProfileCache;
private final TbApiUsageStateService apiUsageStateService; private final TbApiUsageStateService apiUsageStateService;
private final DeviceService deviceService; private final DeviceService deviceService;
private final DeviceProfileService deviceProfileService;
private final RelationService relationService; private final RelationService relationService;
private final DeviceCredentialsService deviceCredentialsService; private final DeviceCredentialsService deviceCredentialsService;
private final DbCallbackExecutorService dbCallbackExecutorService; private final DbCallbackExecutorService dbCallbackExecutorService;
@ -159,6 +166,13 @@ public class DefaultTransportApiService implements TransportApiService {
} else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE); result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
} else if (transportApiRequestMsg.hasValidateProfileX509CertRequestMsg()) {
ValidateDeviceProfileX509CertRequestMsg msg = transportApiRequestMsg.getValidateProfileX509CertRequestMsg();
result = validateDeviceProfileCertificate(msg.getHash());
} else if (transportApiRequestMsg.hasUpdateOrCreateDeviceCertRequestMsg()) {
UpdateOrCreateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getUpdateOrCreateDeviceCertRequestMsg();
DeviceProfile deviceProfile = deviceProfileCache.find(new DeviceProfileId(new UUID(msg.getDeviceProfileIdMSB(), msg.getDeviceProfileIdLSB())));
result = updateOrCreateDeviceCredentials(msg.getHash(), msg.getValue(), msg.getCommonName(), deviceProfile, DeviceCredentialsType.X509_CERTIFICATE);
} else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()); result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
} else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) { } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) {
@ -226,6 +240,31 @@ public class DefaultTransportApiService implements TransportApiService {
} }
} }
private ListenableFuture<TransportApiResponseMsg> validateDeviceProfileCertificate(String credentialsId) {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(credentialsId);
if (deviceProfile != null) {
return getDeviceProfileInfo(deviceProfile);
}
return getEmptyTransportApiResponseFuture();
}
private ListenableFuture<TransportApiResponseMsg> updateOrCreateDeviceCredentials(String credentialsId,
String credentialsValue,
String deviceCN,
DeviceProfile deviceProfile,
DeviceCredentialsType credentialsType) {
String deviceName = extractRegex(deviceCN, deviceProfile.getCertificateRegexPattern());
// find deviceCredentials by deviceName (device exists)
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByTenantIdAndDeviceName(deviceProfile.getTenantId(), deviceName);
if (deviceCredentials != null && deviceCredentials.getCredentialsType() == credentialsType) {
deviceCredentials.setCredentialsId(credentialsId);
deviceCredentials.setCredentialsValue(credentialsValue);
deviceCredentialsService.updateDeviceCredentials(deviceProfile.getTenantId(), deviceCredentials);
return getDeviceInfo(deviceCredentials);
}
return getEmptyTransportApiResponseFuture();
}
private ListenableFuture<TransportApiResponseMsg> validateUserNameCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { private ListenableFuture<TransportApiResponseMsg> validateUserNameCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) {
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());
if (credentials != null) { if (credentials != null) {
@ -507,6 +546,18 @@ public class DefaultTransportApiService implements TransportApiService {
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
} }
private ListenableFuture<TransportApiResponseMsg> getDeviceProfileInfo(DeviceProfile deviceProfile) {
ValidateDeviceProfileCredentialsResponseMsg.Builder builder = ValidateDeviceProfileCredentialsResponseMsg.newBuilder()
.setDeviceProfileIdMSB(deviceProfile.getId().getId().getMostSignificantBits())
.setDeviceProfileIdLSB(deviceProfile.getId().getId().getLeastSignificantBits())
.setIsDeviceProfileFound(true);
return Futures.immediateFuture(
TransportApiResponseMsg.newBuilder()
.setValidateDeviceProfileResponseMsg(builder.build())
.build());
}
private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException { private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException {
DeviceInfoProto.Builder builder = DeviceInfoProto.newBuilder() DeviceInfoProto.Builder builder = DeviceInfoProto.newBuilder()
.setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits()) .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
@ -664,4 +715,13 @@ public class DefaultTransportApiService implements TransportApiService {
private Long checkLong(Long l) { private Long checkLong(Long l) {
return l != null ? l : 0; return l != null ? l : 0;
} }
private String extractRegex(String commonName, String regex) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(commonName);
if (matcher.find()) {
return matcher.group(0);
}
return commonName;
}
} }

View File

@ -169,6 +169,18 @@ message ValidateDeviceX509CertRequestMsg {
string hash = 1; string hash = 1;
} }
message ValidateDeviceProfileX509CertRequestMsg {
string hash = 1;
}
message UpdateOrCreateDeviceX509CertRequestMsg {
string hash = 1;
string value = 2;
string commonName = 3;
int64 deviceProfileIdMSB = 4;
int64 deviceProfileIdLSB = 5;
}
message ValidateBasicMqttCredRequestMsg { message ValidateBasicMqttCredRequestMsg {
string clientId = 1; string clientId = 1;
string userName = 2; string userName = 2;
@ -181,6 +193,12 @@ message ValidateDeviceCredentialsResponseMsg {
bytes profileBody = 3; bytes profileBody = 3;
} }
message ValidateDeviceProfileCredentialsResponseMsg {
int64 deviceProfileIdMSB = 1;
int64 deviceProfileIdLSB = 2;
bool isDeviceProfileFound = 3;
}
message GetOrCreateDeviceFromGatewayRequestMsg { message GetOrCreateDeviceFromGatewayRequestMsg {
int64 gatewayIdMSB = 1; int64 gatewayIdMSB = 1;
int64 gatewayIdLSB = 2; int64 gatewayIdLSB = 2;
@ -885,33 +903,36 @@ message VersionControlResponseMsg {
message TransportApiRequestMsg { message TransportApiRequestMsg {
ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1; ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1;
ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2; ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2;
GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3; ValidateDeviceProfileX509CertRequestMsg validateProfileX509CertRequestMsg = 3;
GetEntityProfileRequestMsg entityProfileRequestMsg = 4; GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 4;
LwM2MRequestMsg lwM2MRequestMsg = 5; GetEntityProfileRequestMsg entityProfileRequestMsg = 5;
ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6; LwM2MRequestMsg lwM2MRequestMsg = 6;
ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 7;
ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 8;
GetResourceRequestMsg resourceRequestMsg = 9; ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 9;
GetOtaPackageRequestMsg otaPackageRequestMsg = 10; GetResourceRequestMsg resourceRequestMsg = 10;
GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11; GetOtaPackageRequestMsg otaPackageRequestMsg = 11;
GetDeviceRequestMsg deviceRequestMsg = 12; GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 12;
GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13; GetDeviceRequestMsg deviceRequestMsg = 13;
GetAllQueueRoutingInfoRequestMsg getAllQueueRoutingInfoRequestMsg = 14; GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 14;
GetAllQueueRoutingInfoRequestMsg getAllQueueRoutingInfoRequestMsg = 15;
UpdateOrCreateDeviceX509CertRequestMsg updateOrCreateDeviceCertRequestMsg = 16;
} }
/* Response from ThingsBoard Core Service to Transport Service */ /* Response from ThingsBoard Core Service to Transport Service */
message TransportApiResponseMsg { message TransportApiResponseMsg {
ValidateDeviceCredentialsResponseMsg validateCredResponseMsg = 1; ValidateDeviceCredentialsResponseMsg validateCredResponseMsg = 1;
GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; ValidateDeviceProfileCredentialsResponseMsg validateDeviceProfileResponseMsg = 2;
GetEntityProfileResponseMsg entityProfileResponseMsg = 3; GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 3;
ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; GetEntityProfileResponseMsg entityProfileResponseMsg = 4;
GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 5;
LwM2MResponseMsg lwM2MResponseMsg = 6; GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 6;
GetResourceResponseMsg resourceResponseMsg = 7; LwM2MResponseMsg lwM2MResponseMsg = 7;
GetOtaPackageResponseMsg otaPackageResponseMsg = 8; GetResourceResponseMsg resourceResponseMsg = 8;
GetDeviceResponseMsg deviceResponseMsg = 9; GetOtaPackageResponseMsg otaPackageResponseMsg = 9;
GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10; GetDeviceResponseMsg deviceResponseMsg = 10;
repeated GetQueueRoutingInfoResponseMsg getQueueRoutingInfoResponseMsgs = 11; GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 11;
repeated GetQueueRoutingInfoResponseMsg getQueueRoutingInfoResponseMsgs = 12;
} }
/* Messages that are handled by ThingsBoard Core Service */ /* Messages that are handled by ThingsBoard Core Service */

View File

@ -15,10 +15,10 @@
*/ */
package org.thingsboard.server.dao.device; package org.thingsboard.server.dao.device;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import com.fasterxml.jackson.databind.JsonNode;
public interface DeviceCredentialsService { public interface DeviceCredentialsService {
@ -26,6 +26,8 @@ public interface DeviceCredentialsService {
DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId); DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId);
DeviceCredentials findDeviceCredentialsByTenantIdAndDeviceName(TenantId tenantId, String deviceName);
DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);

View File

@ -39,6 +39,8 @@ public interface DeviceProfileService extends EntityDaoService {
PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType); PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType);
DeviceProfile findDeviceProfileByCertificateHash(String credentialsId);
DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName); DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName);
DeviceProfile createDefaultDeviceProfile(TenantId tenantId); DeviceProfile createDefaultDeviceProfile(TenantId tenantId);

View File

@ -16,7 +16,6 @@
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
@ -28,7 +27,6 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.Length;
@ -68,6 +66,13 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private DeviceTransportType transportType; private DeviceTransportType transportType;
@ApiModelProperty(position = 15, value = "Provisioning strategy.") @ApiModelProperty(position = 15, value = "Provisioning strategy.")
private DeviceProfileProvisionType provisionType; private DeviceProfileProvisionType provisionType;
@ApiModelProperty(position = 16, value = "CA certificate value")
private String certificateValue;
@ApiModelProperty(position = 17, value = "CA certificate hash")
private String certificateHash;
@ApiModelProperty(position = 18, value = "Regex for fetch deviceName from CN")
private String certificateRegexPattern;
@ApiModelProperty(position = 7, value = "Reference to the rule chain. " + @ApiModelProperty(position = 7, value = "Reference to the rule chain. " +
"If present, the specified rule chain will be used to process all messages related to device, including telemetry, attribute updates, etc. " + "If present, the specified rule chain will be used to process all messages related to device, including telemetry, attribute updates, etc. " +
"Otherwise, the root rule chain will be used to process those messages.") "Otherwise, the root rule chain will be used to process those messages.")

View File

@ -24,12 +24,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.auth.ValidateDeviceProfileCredentialsResponse;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials; import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig; import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.common.transport.util.SslUtil;
@ -142,8 +143,15 @@ public class MqttSslHandlerProvider {
} }
@Override @Override
public void checkClientTrusted(X509Certificate[] chain, public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
String authType) throws CertificateException { String deviceCN = SslUtil.parseCommonName(chain[0]);
String deviceCert;
String deviceCredentialsValue = SslUtil.getCertificateString(chain[0]);
try {
deviceCert = EncryptionUtil.getSha3Hash(SslUtil.getCertificateString(chain[0]));
} catch (CertificateEncodingException e) {
throw new RuntimeException(e);
}
String credentialsBody = null; String credentialsBody = null;
for (X509Certificate cert : chain) { for (X509Certificate cert : chain) {
try { try {
@ -152,13 +160,53 @@ public class MqttSslHandlerProvider {
final String[] credentialsBodyHolder = new String[1]; final String[] credentialsBodyHolder = new String[1];
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
transportService.process(DeviceTransportType.MQTT, TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(), transportService.process(DeviceTransportType.MQTT, TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
new TransportServiceCallback<ValidateDeviceCredentialsResponse>() { new TransportServiceCallback<>() {
@Override @Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) { public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (!StringUtils.isEmpty(msg.getCredentials())) { if (!StringUtils.isEmpty(msg.getCredentials())) {
credentialsBodyHolder[0] = msg.getCredentials(); credentialsBodyHolder[0] = msg.getCredentials();
latch.countDown();
} else {
transportService.process(DeviceTransportType.MQTT,
TransportProtos.ValidateDeviceProfileX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceProfileCredentialsResponse msg) {
if (msg.isDeviceProfileFound()) {
transportService.process(DeviceTransportType.MQTT,
TransportProtos.UpdateOrCreateDeviceX509CertRequestMsg.newBuilder()
.setHash(deviceCert)
.setCommonName(deviceCN)
.setDeviceProfileIdMSB(msg.getDeviceProfileId().getId().getMostSignificantBits())
.setDeviceProfileIdLSB(msg.getDeviceProfileId().getId().getLeastSignificantBits())
.build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
System.out.println("msg.getCredentials() = " + msg.getCredentials());
credentialsBodyHolder[0] = msg.getCredentials();
latch.countDown();
}
@Override
public void onError(Throwable e) {
log.error(e.getMessage(), e);
latch.countDown();
}
}
);
} else {
latch.countDown();
}
}
@Override
public void onError(Throwable e) {
log.error(e.getMessage(), e);
latch.countDown();
}
});
} }
latch.countDown();
} }
@Override @Override
@ -168,7 +216,7 @@ public class MqttSslHandlerProvider {
} }
}); });
latch.await(10, TimeUnit.SECONDS); latch.await(10, TimeUnit.SECONDS);
if (strCert.equals(credentialsBodyHolder[0])) { if (deviceCredentialsValue.equals(credentialsBodyHolder[0])) {
credentialsBody = credentialsBodyHolder[0]; credentialsBody = credentialsBodyHolder[0];
break; break;
} }

View File

@ -21,7 +21,6 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.mqtt.MqttConnAckMessage; import io.netty.handler.codec.mqtt.MqttConnAckMessage;
import io.netty.handler.codec.mqtt.MqttConnAckVariableHeader;
import io.netty.handler.codec.mqtt.MqttConnectMessage; import io.netty.handler.codec.mqtt.MqttConnectMessage;
import io.netty.handler.codec.mqtt.MqttConnectReturnCode; import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
import io.netty.handler.codec.mqtt.MqttFixedHeader; import io.netty.handler.codec.mqtt.MqttFixedHeader;
@ -63,12 +62,12 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.auth.ValidateDeviceProfileCredentialsResponse;
import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.common.transport.service.DefaultTransportService;
import org.thingsboard.server.common.transport.service.SessionMetaData; import org.thingsboard.server.common.transport.service.SessionMetaData;
import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
@ -80,7 +79,7 @@ import org.thingsboard.server.transport.mqtt.util.ReturnCodeResolver;
import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -90,16 +89,15 @@ import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static com.amazonaws.util.StringUtils.UTF8; import static com.amazonaws.util.StringUtils.UTF8;
import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK;
import static io.netty.handler.codec.mqtt.MqttMessageType.CONNECT; import static io.netty.handler.codec.mqtt.MqttMessageType.CONNECT;
import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP;
import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK;
import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED;
@ -810,9 +808,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
deviceSessionCtx.setProvisionOnly(true); deviceSessionCtx.setProvisionOnly(true);
ctx.writeAndFlush(createMqttConnAckMsg(ReturnCode.SUCCESS, msg)); ctx.writeAndFlush(createMqttConnAckMsg(ReturnCode.SUCCESS, msg));
} else { } else {
X509Certificate cert; X509Certificate[] chain;
if (sslHandler != null && (cert = getX509Certificate()) != null) { if (sslHandler != null && (chain = getX509Certificate()) != null) {
processX509CertConnect(ctx, cert, msg); processX509CertConnect(ctx, chain, msg);
} else { } else {
processAuthTokenConnect(ctx, msg); processAuthTokenConnect(ctx, msg);
} }
@ -848,27 +846,82 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
}); });
} }
private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert, MqttConnectMessage connectMessage) { private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate[] chain, MqttConnectMessage connectMessage) {
try { try {
if (!context.isSkipValidityCheckForClientCert()) { String deviceCN = SslUtil.parseCommonName(chain[0]);
cert.checkValidity(); String deviceCertHash = EncryptionUtil.getSha3Hash(SslUtil.getCertificateString(chain[0]));
} for (X509Certificate cert : chain) {
String strCert = SslUtil.getCertificateString(cert); try {
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); String strCert = SslUtil.getCertificateString(cert);
transportService.process(DeviceTransportType.MQTT, ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(), String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
new TransportServiceCallback<>() { final ValidateDeviceCredentialsResponse[] validateDeviceCredentialsResponses = new ValidateDeviceCredentialsResponse[1];
@Override CountDownLatch latch = new CountDownLatch(1);
public void onSuccess(ValidateDeviceCredentialsResponse msg) { transportService.process(DeviceTransportType.MQTT, TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
onValidateDeviceResponse(msg, ctx, connectMessage); new TransportServiceCallback<>() {
} @Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (!StringUtils.isEmpty(msg.getCredentials())) {
validateDeviceCredentialsResponses[0] = msg;
latch.countDown();
} else {
transportService.process(DeviceTransportType.MQTT,
TransportProtos.ValidateDeviceProfileX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceProfileCredentialsResponse msg) {
if (msg.isDeviceProfileFound()) {
transportService.process(DeviceTransportType.MQTT,
TransportProtos.UpdateOrCreateDeviceX509CertRequestMsg.newBuilder()
.setHash(deviceCertHash)
.setCommonName(deviceCN)
.setDeviceProfileIdMSB(msg.getDeviceProfileId().getId().getMostSignificantBits())
.setDeviceProfileIdLSB(msg.getDeviceProfileId().getId().getLeastSignificantBits())
.build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (!StringUtils.isEmpty(msg.getCredentials())) {
validateDeviceCredentialsResponses[0] = msg;
latch.countDown();
}
}
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
log.trace("[{}] Failed to process credentials: {}", address, sha3Hash, e); log.error(e.getMessage(), e);
ctx.writeAndFlush(createMqttConnAckMsg(ReturnCode.SERVER_UNAVAILABLE_5, connectMessage)); latch.countDown();
ctx.close(); }
} }
}); );
} else {
latch.countDown();
}
}
@Override
public void onError(Throwable e) {
log.error(e.getMessage(), e);
latch.countDown();
}
});
}
}
@Override
public void onError(Throwable e) {
log.error(e.getMessage(), e);
latch.countDown();
}
});
latch.await(10, TimeUnit.SECONDS);
if (validateDeviceCredentialsResponses[0] != null && validateDeviceCredentialsResponses[0].hasDeviceInfo()) {
onValidateDeviceResponse(validateDeviceCredentialsResponses[0], ctx, connectMessage);
break;
}
} catch (InterruptedException | CertificateEncodingException e) {
log.error(e.getMessage(), e);
}
}
} catch (Exception e) { } catch (Exception e) {
context.onAuthFailure(address); context.onAuthFailure(address);
ctx.writeAndFlush(createMqttConnAckMsg(ReturnCode.NOT_AUTHORIZED_5, connectMessage)); ctx.writeAndFlush(createMqttConnAckMsg(ReturnCode.NOT_AUTHORIZED_5, connectMessage));
@ -877,17 +930,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
} }
} }
private X509Certificate getX509Certificate() { private X509Certificate[] getX509Certificate() {
try { try {
Certificate[] certChain = sslHandler.engine().getSession().getPeerCertificates(); return (X509Certificate[]) sslHandler.engine().getSession().getPeerCertificates();
if (certChain.length > 0) {
return (X509Certificate) certChain[0];
}
} catch (SSLPeerUnverifiedException e) { } catch (SSLPeerUnverifiedException e) {
log.warn(e.getMessage()); log.warn(e.getMessage());
return null; return null;
} }
return null;
} }
private MqttConnAckMessage createMqttConnAckMsg(ReturnCode returnCode, MqttConnectMessage msg) { private MqttConnAckMessage createMqttConnAckMsg(ReturnCode returnCode, MqttConnectMessage msg) {

View File

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.auth.ValidateDeviceProfileCredentialsResponse;
import org.thingsboard.server.common.transport.service.SessionMetaData; import org.thingsboard.server.common.transport.service.SessionMetaData;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
@ -52,8 +53,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMs
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.gen.transport.TransportProtos.UpdateOrCreateDeviceX509CertRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceProfileX509CertRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
@ -87,6 +90,12 @@ public interface TransportService {
void process(DeviceTransportType transportType, ValidateDeviceX509CertRequestMsg msg, void process(DeviceTransportType transportType, ValidateDeviceX509CertRequestMsg msg,
TransportServiceCallback<ValidateDeviceCredentialsResponse> callback); TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
void process(DeviceTransportType transportType, ValidateDeviceProfileX509CertRequestMsg msg,
TransportServiceCallback<ValidateDeviceProfileCredentialsResponse> callback);
void process(DeviceTransportType transportType, UpdateOrCreateDeviceX509CertRequestMsg msg,
TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
void process(ValidateDeviceLwM2MCredentialsRequestMsg msg, void process(ValidateDeviceLwM2MCredentialsRequestMsg msg,
TransportServiceCallback<ValidateDeviceCredentialsResponse> callback); TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);

View File

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.auth;
import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@Data
@Builder
public class ValidateDeviceProfileCredentialsResponse {
private final DeviceProfileId deviceProfileId;
private final boolean isDeviceProfileFound;
}

View File

@ -71,8 +71,8 @@ import org.thingsboard.server.common.transport.TransportTenantProfileCache;
import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse;
import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.auth.ValidateDeviceProfileCredentialsResponse;
import org.thingsboard.server.common.transport.limits.TransportRateLimitService; import org.thingsboard.server.common.transport.limits.TransportRateLimitService;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
@ -97,6 +97,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.provider.TbTransportQueueFactory; import org.thingsboard.server.queue.provider.TbTransportQueueFactory;
import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbTransportComponent; import org.thingsboard.server.queue.util.TbTransportComponent;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -427,6 +428,32 @@ public class DefaultTransportService implements TransportService {
AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor);
} }
@Override
public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceProfileX509CertRequestMsg requestMsg,
TransportServiceCallback<ValidateDeviceProfileCredentialsResponse> callback) {
log.trace("Processing msg: {}", requestMsg);
TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(),
TransportApiRequestMsg.newBuilder().setValidateProfileX509CertRequestMsg(requestMsg).build());
ListenableFuture<ValidateDeviceProfileCredentialsResponse> response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> {
TransportProtos.ValidateDeviceProfileCredentialsResponseMsg msg = tmp.getValue().getValidateDeviceProfileResponseMsg();
ValidateDeviceProfileCredentialsResponse.ValidateDeviceProfileCredentialsResponseBuilder result = ValidateDeviceProfileCredentialsResponse.builder();
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(msg.getDeviceProfileIdMSB(), msg.getDeviceProfileIdLSB()));
result.deviceProfileId(deviceProfileId);
result.isDeviceProfileFound(msg.getIsDeviceProfileFound());
return result.build();
}, MoreExecutors.directExecutor());
AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor);
}
@Override
public void process(DeviceTransportType transportType, TransportProtos.UpdateOrCreateDeviceX509CertRequestMsg requestMsg,
TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
log.trace("Processing msg: {}", requestMsg);
TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder()
.setUpdateOrCreateDeviceCertRequestMsg(requestMsg).build());
doProcess(transportType, protoMsg, callback);
}
@Override @Override
public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) { public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
log.trace("Processing msg: {}", msg); log.trace("Processing msg: {}", msg);

View File

@ -16,11 +16,17 @@
package org.thingsboard.server.common.transport.util; package org.thingsboard.server.common.transport.util;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.springframework.util.Base64Utils; import org.springframework.util.Base64Utils;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
/** /**
* @author Valerii Sosliuk * @author Valerii Sosliuk
@ -35,4 +41,15 @@ public class SslUtil {
throws CertificateEncodingException { throws CertificateEncodingException {
return EncryptionUtil.certTrimNewLines(Base64Utils.encodeToString(cert.getEncoded())); return EncryptionUtil.certTrimNewLines(Base64Utils.encodeToString(cert.getEncoded()));
} }
public static String parseCommonName(X509Certificate certificate) {
X500Name x500name;
try {
x500name = new JcaX509CertificateHolder(certificate).getSubject();
} catch (CertificateEncodingException e) {
throw new RuntimeException(e);
}
RDN cn = x500name.getRDNs(BCStyle.CN)[0];
return IETFUtils.valueToString(cn.getFirst().getValue());
}
} }

View File

@ -53,4 +53,12 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
*/ */
DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId); DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId);
/**
* Find device credentials by device name.
*
* @param deviceName the device name
* @return the device credentials object
*/
DeviceCredentials findByTenantIdAndDeviceName(TenantId tenantId, String deviceName);
} }

View File

@ -16,15 +16,12 @@
package org.thingsboard.server.dao.device; package org.thingsboard.server.dao.device;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.util.SecurityUtil; import org.eclipse.leshan.core.util.SecurityUtil;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
@ -86,6 +83,13 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService<St
false); false);
} }
@Override
public DeviceCredentials findDeviceCredentialsByTenantIdAndDeviceName(TenantId tenantId, String deviceName) {
log.trace("Executing findDeviceCredentialsByDeviceName [{}]", deviceName);
validateString(deviceName, "Incorrect deviceName " + deviceName);
return deviceCredentialsDao.findByTenantIdAndDeviceName(tenantId, deviceName);
}
@Override @Override
public DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) { public DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
return saveOrUpdate(tenantId, deviceCredentials); return saveOrUpdate(tenantId, deviceCredentials);

View File

@ -29,25 +29,31 @@ public class DeviceProfileCacheKey implements Serializable {
private final TenantId tenantId; private final TenantId tenantId;
private final String name; private final String name;
private final DeviceProfileId deviceProfileId; private final DeviceProfileId deviceProfileId;
private final String certificateHash;
private final boolean defaultProfile; private final boolean defaultProfile;
private DeviceProfileCacheKey(TenantId tenantId, String name, DeviceProfileId deviceProfileId, boolean defaultProfile) { private DeviceProfileCacheKey(TenantId tenantId, String name, DeviceProfileId deviceProfileId, String certificateHash, boolean defaultProfile) {
this.tenantId = tenantId; this.tenantId = tenantId;
this.name = name; this.name = name;
this.deviceProfileId = deviceProfileId; this.deviceProfileId = deviceProfileId;
this.certificateHash = certificateHash;
this.defaultProfile = defaultProfile; this.defaultProfile = defaultProfile;
} }
public static DeviceProfileCacheKey fromName(TenantId tenantId, String name) { public static DeviceProfileCacheKey fromName(TenantId tenantId, String name) {
return new DeviceProfileCacheKey(tenantId, name, null, false); return new DeviceProfileCacheKey(tenantId, name, null, null, false);
} }
public static DeviceProfileCacheKey fromId(DeviceProfileId id) { public static DeviceProfileCacheKey fromId(DeviceProfileId id) {
return new DeviceProfileCacheKey(null, null, id, false); return new DeviceProfileCacheKey(null, null, id, null, false);
}
public static DeviceProfileCacheKey fromCertificateHash(String certificateHash) {
return new DeviceProfileCacheKey(null, null, null, certificateHash, false);
} }
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) { public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
return new DeviceProfileCacheKey(tenantId, null, null, true); return new DeviceProfileCacheKey(tenantId, null, null, null, true);
} }
@Override @Override

View File

@ -45,4 +45,6 @@ public interface DeviceProfileDao extends Dao<DeviceProfile>, ExportableEntityDa
DeviceProfile findByProvisionDeviceKey(String provisionDeviceKey); DeviceProfile findByProvisionDeviceKey(String provisionDeviceKey);
DeviceProfile findByName(TenantId tenantId, String profileName); DeviceProfile findByName(TenantId tenantId, String profileName);
DeviceProfile findByCertificateHash(String certificateHash);
} }

View File

@ -53,6 +53,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service("DeviceProfileDaoService") @Service("DeviceProfileDaoService")
@Slf4j @Slf4j
@ -61,6 +62,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId "; private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
private static final String INCORRECT_DEVICE_PROFILE_NAME = "Incorrect deviceProfileName "; private static final String INCORRECT_DEVICE_PROFILE_NAME = "Incorrect deviceProfileName ";
private static final String INCORRECT_DEVICE_PROFILE_CREDENTIALS_HASH = "Incorrect deviceProfileCredentialsHash ";
private static final String DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS = "Device profile with such name already exists!"; private static final String DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS = "Device profile with such name already exists!";
@Autowired @Autowired
@ -197,6 +199,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType); return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType);
} }
@Override
public DeviceProfile findDeviceProfileByCertificateHash(String certificateHash) {
log.trace("Executing findDeviceProfileIdByCredentialsId credentialId [{}]", certificateHash);
validateString(certificateHash, INCORRECT_DEVICE_PROFILE_CREDENTIALS_HASH + certificateHash);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromCertificateHash(certificateHash),
() -> deviceProfileDao.findByCertificateHash(certificateHash), true);
}
@Override @Override
public DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String name) { public DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String name) {
log.trace("Executing findOrCreateDefaultDeviceProfile"); log.trace("Executing findOrCreateDefaultDeviceProfile");

View File

@ -16,6 +16,8 @@
package org.thingsboard.server.dao.sql.device; package org.thingsboard.server.dao.sql.device;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity; import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity;
import java.util.UUID; import java.util.UUID;
@ -28,4 +30,7 @@ public interface DeviceCredentialsRepository extends JpaRepository<DeviceCredent
DeviceCredentialsEntity findByDeviceId(UUID deviceId); DeviceCredentialsEntity findByDeviceId(UUID deviceId);
DeviceCredentialsEntity findByCredentialsId(String credentialsId); DeviceCredentialsEntity findByCredentialsId(String credentialsId);
@Query("SELECT dc FROM DeviceCredentialsEntity dc INNER JOIN DeviceEntity de ON dc.deviceId = de.id WHERE de.tenantId = :tenantId AND de.name = :deviceName ")
DeviceCredentialsEntity findByTenantIdAndDeviceName(@Param("tenantId") UUID tenantId, @Param("deviceName") String deviceName);
} }

View File

@ -64,6 +64,8 @@ public interface DeviceProfileRepository extends JpaRepository<DeviceProfileEnti
"WHERE d.tenantId = :tenantId AND d.isDefault = true") "WHERE d.tenantId = :tenantId AND d.isDefault = true")
DeviceProfileInfo findDefaultDeviceProfileInfo(@Param("tenantId") UUID tenantId); DeviceProfileInfo findDefaultDeviceProfileInfo(@Param("tenantId") UUID tenantId);
DeviceProfileEntity findDeviceProfileByCertificateHash(String certificateHash);
DeviceProfileEntity findByTenantIdAndName(UUID id, String profileName); DeviceProfileEntity findByTenantIdAndName(UUID id, String profileName);
DeviceProfileEntity findByProvisionDeviceKey(@Param("provisionDeviceKey") String provisionDeviceKey); DeviceProfileEntity findByProvisionDeviceKey(@Param("provisionDeviceKey") String provisionDeviceKey);

View File

@ -66,4 +66,9 @@ public class JpaDeviceCredentialsDao extends JpaAbstractDao<DeviceCredentialsEnt
public DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId) { public DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId) {
return DaoUtil.getData(deviceCredentialsRepository.findByCredentialsId(credentialsId)); return DaoUtil.getData(deviceCredentialsRepository.findByCredentialsId(credentialsId));
} }
@Override
public DeviceCredentials findByTenantIdAndDeviceName(TenantId tenantId, String deviceName) {
return DaoUtil.getData(deviceCredentialsRepository.findByTenantIdAndDeviceName(tenantId, deviceName));
}
} }

View File

@ -115,6 +115,11 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE
return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName)); return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName));
} }
@Override
public DeviceProfile findByCertificateHash(String certificateHash) {
return DaoUtil.getData(deviceProfileRepository.findDeviceProfileByCertificateHash(certificateHash));
}
@Override @Override
public DeviceProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { public DeviceProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) {
return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndExternalId(tenantId, externalId)); return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndExternalId(tenantId, externalId));