Refactor x509: add logic of rotating x509 credentials and creating new device to DeviceProvisionService
This commit is contained in:
parent
d1a21900f0
commit
55adb3d12a
@ -424,16 +424,6 @@ $$;
|
||||
-- ALARM FUNCTIONS END
|
||||
|
||||
|
||||
-- DEVICE PROFILE CERTIFICATE START
|
||||
|
||||
ALTER TABLE device_profile
|
||||
ADD COLUMN IF NOT EXISTS certificate_hash varchar,
|
||||
DROP CONSTRAINT IF EXISTS device_profile_credentials_hash_unq_key,
|
||||
ADD CONSTRAINT device_profile_credentials_hash_unq_key UNIQUE (certificate_hash);
|
||||
|
||||
-- DEVICE PROFILE CERTIFICATE END
|
||||
|
||||
|
||||
-- TTL DROP PARTITIONS FUNCTIONS UPDATE START
|
||||
|
||||
DROP PROCEDURE IF EXISTS drop_partitions_by_max_ttl(character varying, bigint, bigint);
|
||||
|
||||
@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
@ -29,6 +28,7 @@ import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.audit.ActionType;
|
||||
import org.thingsboard.server.common.data.device.profile.X509CertificateChainProvisionConfiguration;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
@ -36,15 +36,19 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||
import org.thingsboard.server.common.msg.EncryptionUtil;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
|
||||
import org.thingsboard.server.common.transport.util.SslUtil;
|
||||
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||
import org.thingsboard.server.dao.audit.AuditLogService;
|
||||
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
||||
import org.thingsboard.server.dao.device.DeviceDao;
|
||||
import org.thingsboard.server.dao.device.DeviceProfileDao;
|
||||
import org.thingsboard.server.dao.device.DeviceProfileService;
|
||||
import org.thingsboard.server.dao.device.DeviceProvisionService;
|
||||
import org.thingsboard.server.dao.device.DeviceService;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
|
||||
@ -77,35 +81,23 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
||||
private static final String DEVICE_PROVISION_STATE = "provisionState";
|
||||
private static final String PROVISIONED_STATE = "provisioned";
|
||||
|
||||
@Autowired
|
||||
TbClusterService clusterService;
|
||||
private final TbClusterService clusterService;
|
||||
private final DeviceProfileService deviceProfileService;
|
||||
private final DeviceService deviceService;
|
||||
private final DeviceCredentialsService deviceCredentialsService;
|
||||
private final AttributesService attributesService;
|
||||
private final AuditLogService auditLogService;
|
||||
private final PartitionService partitionService;
|
||||
|
||||
@Autowired
|
||||
DeviceDao deviceDao;
|
||||
|
||||
@Autowired
|
||||
DeviceProfileDao deviceProfileDao;
|
||||
|
||||
@Autowired
|
||||
DeviceService deviceService;
|
||||
|
||||
@Autowired
|
||||
DeviceCredentialsService deviceCredentialsService;
|
||||
|
||||
@Autowired
|
||||
AttributesService attributesService;
|
||||
|
||||
@Autowired
|
||||
DeviceStateService deviceStateService;
|
||||
|
||||
@Autowired
|
||||
AuditLogService auditLogService;
|
||||
|
||||
@Autowired
|
||||
PartitionService partitionService;
|
||||
|
||||
public DeviceProvisionServiceImpl(TbQueueProducerProvider producerProvider) {
|
||||
public DeviceProvisionServiceImpl(TbQueueProducerProvider producerProvider, TbClusterService clusterService, DeviceDao deviceDao, DeviceProfileDao deviceProfileDao, DeviceProfileService deviceProfileService, DeviceService deviceService, DeviceCredentialsService deviceCredentialsService, AttributesService attributesService, DeviceStateService deviceStateService, AuditLogService auditLogService, PartitionService partitionService) {
|
||||
ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer();
|
||||
this.clusterService = clusterService;
|
||||
this.deviceProfileService = deviceProfileService;
|
||||
this.deviceService = deviceService;
|
||||
this.deviceCredentialsService = deviceCredentialsService;
|
||||
this.attributesService = attributesService;
|
||||
this.auditLogService = auditLogService;
|
||||
this.partitionService = partitionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -124,14 +116,14 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
||||
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
||||
}
|
||||
|
||||
DeviceProfile targetProfile = deviceProfileDao.findByProvisionDeviceKey(provisionRequestKey);
|
||||
DeviceProfile targetProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(provisionRequestKey);
|
||||
|
||||
if (targetProfile == null || targetProfile.getProfileData().getProvisionConfiguration() == null ||
|
||||
targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() == null) {
|
||||
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
||||
}
|
||||
|
||||
Device targetDevice = deviceDao.findDeviceByTenantIdAndName(targetProfile.getTenantId().getId(), provisionRequest.getDeviceName()).orElse(null);
|
||||
Device targetDevice = deviceService.findDeviceByTenantIdAndName(targetProfile.getTenantId(), provisionRequest.getDeviceName());
|
||||
|
||||
switch (targetProfile.getProvisionType()) {
|
||||
case ALLOW_CREATE_NEW_DEVICES:
|
||||
@ -155,6 +147,22 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case X509_CERTIFICATE_CHAIN:
|
||||
X509CertificateChainProvisionConfiguration x509Configuration = (X509CertificateChainProvisionConfiguration) targetProfile.getProfileData().getProvisionConfiguration();
|
||||
if (targetDevice != null && targetDevice.getDeviceProfileId().equals(targetProfile.getId())) {
|
||||
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(targetDevice.getTenantId(), targetDevice.getId());
|
||||
if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
|
||||
String updatedDeviceCertificateValue = provisionRequest.getCredentialsData().getX509CertHash();
|
||||
deviceCredentials = updateDeviceCredentials(targetDevice.getTenantId(), deviceCredentials,
|
||||
updatedDeviceCertificateValue, DeviceCredentialsType.X509_CERTIFICATE);
|
||||
}
|
||||
return new ProvisionResponse(deviceCredentials, ProvisionResponseStatus.SUCCESS);
|
||||
} else if (x509Configuration.isAllowCreateNewDevicesByX509Certificate()) {
|
||||
return createDevice(provisionRequest, targetProfile);
|
||||
} else {
|
||||
log.info("Device doesn't exist and cannot be created due incorrect configuration for X509CertificateChainProvisionConfiguration");
|
||||
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
||||
}
|
||||
}
|
||||
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
||||
}
|
||||
@ -209,6 +217,14 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
||||
}
|
||||
}
|
||||
|
||||
private DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials, String certificateValue,
|
||||
DeviceCredentialsType credentialsType) {
|
||||
log.trace("Updating device credentials [{}] with certificate value [{}]", deviceCredentials, certificateValue);
|
||||
deviceCredentials.setCredentialsValue(certificateValue);
|
||||
deviceCredentials.setCredentialsType(credentialsType);
|
||||
return deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials);
|
||||
}
|
||||
|
||||
private ListenableFuture<List<String>> saveProvisionStateAttribute(Device device) {
|
||||
return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
|
||||
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE),
|
||||
|
||||
@ -25,7 +25,6 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.protobuf.ByteString;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
|
||||
@ -34,7 +33,6 @@ import org.thingsboard.server.common.data.ApiUsageState;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
|
||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.OtaPackage;
|
||||
@ -49,6 +47,7 @@ import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfigu
|
||||
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.data.PowerMode;
|
||||
import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileProvisionConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
|
||||
import org.thingsboard.server.common.data.device.profile.X509CertificateChainProvisionConfiguration;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
@ -76,6 +75,7 @@ import org.thingsboard.server.dao.device.DeviceService;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
|
||||
import org.thingsboard.server.dao.ota.OtaPackageService;
|
||||
import org.thingsboard.server.dao.queue.QueueService;
|
||||
import org.thingsboard.server.dao.relation.RelationService;
|
||||
@ -106,10 +106,6 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
||||
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
|
||||
import org.thingsboard.server.service.resource.TbResourceService;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -250,38 +246,13 @@ public class DefaultTransportApiService implements TransportApiService {
|
||||
if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
|
||||
return getDeviceInfo(credentials);
|
||||
}
|
||||
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(certificateHash);
|
||||
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(certificateHash);
|
||||
if (deviceProfile != null) {
|
||||
X509CertificateChainProvisionConfiguration x509Configuration;
|
||||
if (deviceProfile.getProfileData().getProvisionConfiguration() instanceof X509CertificateChainProvisionConfiguration) {
|
||||
x509Configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
|
||||
} else {
|
||||
log.warn("Device Profile provision configuration is not X509CertificateChainProvisionConfiguration");
|
||||
return getEmptyTransportApiResponseFuture();
|
||||
}
|
||||
String deviceName = extractDeviceNameFromCertificateCNByRegEx(chain.get(0), x509Configuration.getCertificateRegExPattern());
|
||||
if (deviceName == null) {
|
||||
log.warn("Cannot extract device name from device's CN using regex [{}]", x509Configuration.getCertificateRegExPattern());
|
||||
return getEmptyTransportApiResponseFuture();
|
||||
}
|
||||
Device device = deviceService.findDeviceByTenantIdAndName(deviceProfile.getTenantId(), deviceName);
|
||||
String updateDeviceCertificateValue = chain.get(0);
|
||||
String updateDeviceCertificateHash = EncryptionUtil.getSha3Hash(updateDeviceCertificateValue);
|
||||
if (device != null) {
|
||||
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
|
||||
if (deviceCredentials != null && deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
|
||||
deviceCredentials = updateDeviceCredentials(device.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, DeviceCredentialsType.X509_CERTIFICATE);
|
||||
} else if (deviceCredentials == null) {
|
||||
deviceCredentials = createDeviceCredentials(device.getTenantId(), device.getId(), updateDeviceCertificateValue, updateDeviceCertificateHash, DeviceCredentialsType.X509_CERTIFICATE);
|
||||
}
|
||||
return getDeviceInfo(deviceCredentials);
|
||||
} else if (deviceProfile.getProvisionType() == DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN && x509Configuration.isAllowCreateNewDevicesByX509Certificate()) {
|
||||
Device savedDevice = createDevice(deviceProfile.getTenantId(), deviceProfile.getId(), deviceName, deviceProfile.getName());
|
||||
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getTenantId(), savedDevice.getId());
|
||||
deviceCredentials = updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, DeviceCredentialsType.X509_CERTIFICATE);
|
||||
return getDeviceInfo(deviceCredentials);
|
||||
} else {
|
||||
log.info("Device doesn't exist and cannot be created due incorrect configuration for X509CertificateChainProvisionConfiguration");
|
||||
String updatedDeviceProvisionSecret = chain.get(0);
|
||||
ProvisionRequest provisionRequest = createProvisionRequest(deviceProfile, updatedDeviceProvisionSecret);
|
||||
ProvisionResponse provisionResponse = deviceProvisionService.provisionDevice(provisionRequest);
|
||||
if (provisionResponse.getResponseStatus().equals(ProvisionResponseStatus.SUCCESS)) {
|
||||
return getDeviceInfo(provisionResponse.getDeviceCredentials());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -728,9 +699,37 @@ public class DefaultTransportApiService implements TransportApiService {
|
||||
return l != null ? l : 0;
|
||||
}
|
||||
|
||||
private ProvisionRequest createProvisionRequest(DeviceProfile deviceProfile, String certificateValue) {
|
||||
String deviceName = getDeviceName(deviceProfile, certificateValue);
|
||||
|
||||
ProvisionDeviceProfileCredentials provisionDeviceProfileCredentials = new ProvisionDeviceProfileCredentials(
|
||||
deviceProfile.getProvisionDeviceKey(),
|
||||
deviceProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret()
|
||||
);
|
||||
ProvisionDeviceCredentialsData provisionDeviceCredentialsData = new ProvisionDeviceCredentialsData(null, null, null, null, certificateValue);
|
||||
|
||||
return new ProvisionRequest(deviceName, DeviceCredentialsType.X509_CERTIFICATE, provisionDeviceCredentialsData, provisionDeviceProfileCredentials);
|
||||
}
|
||||
|
||||
|
||||
private String getDeviceName(DeviceProfile deviceProfile, String certificateValue) {
|
||||
X509CertificateChainProvisionConfiguration configuration = new X509CertificateChainProvisionConfiguration();
|
||||
String deviceName = null;
|
||||
if (deviceProfile.getProfileData().getProvisionConfiguration() instanceof X509CertificateChainProvisionConfiguration) {
|
||||
configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
|
||||
deviceName = extractDeviceNameFromCertificateCNByRegEx(certificateValue, configuration.getCertificateRegExPattern());
|
||||
if (deviceName == null) {
|
||||
log.warn("Cannot extract device name from device's CN using regex [{}]", configuration.getCertificateRegExPattern());
|
||||
}
|
||||
} else {
|
||||
log.warn("Device Profile configuration: expected [{}], actual [{}]", configuration.getType(), deviceProfile.getProvisionType());
|
||||
}
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
private String extractDeviceNameFromCertificateCNByRegEx(String x509Value, String regex) {
|
||||
try {
|
||||
String commonName = SslUtil.parseCommonName(readCertFile(x509Value));
|
||||
String commonName = SslUtil.parseCommonName(SslUtil.readCertFile(x509Value));
|
||||
log.trace("Extract CN [{}] by regex pattern [{}]", commonName, regex);
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(commonName);
|
||||
@ -751,53 +750,4 @@ public class DefaultTransportApiService implements TransportApiService {
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
private X509Certificate readCertFile(String fileContent) {
|
||||
X509Certificate certificate = null;
|
||||
try {
|
||||
if (fileContent != null && !fileContent.trim().isEmpty()) {
|
||||
fileContent = fileContent.replace("-----BEGIN CERTIFICATE-----", "")
|
||||
.replace("-----END CERTIFICATE-----", "")
|
||||
.replaceAll("\\s", "");
|
||||
byte[] decoded = Base64.decodeBase64(fileContent);
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
try (InputStream inStream = new ByteArrayInputStream(decoded)) {
|
||||
certificate = (X509Certificate) certFactory.generateCertificate(inStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return certificate;
|
||||
}
|
||||
|
||||
private DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials, String certificateValue,
|
||||
String certificateHash, DeviceCredentialsType credentialsType) {
|
||||
log.trace("Updating device credentials [{}] with certificate id [{}]", deviceCredentials, certificateHash);
|
||||
deviceCredentials.setCredentialsId(certificateHash);
|
||||
deviceCredentials.setCredentialsValue(certificateValue);
|
||||
deviceCredentials.setCredentialsType(credentialsType);
|
||||
return deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials);
|
||||
}
|
||||
|
||||
private DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceId deviceId, String certificateValue,
|
||||
String certificateHash, DeviceCredentialsType credentialsType) {
|
||||
log.trace("Creating new deviceCredentials for device [{}] with certificate id [{}]", deviceId, certificateHash);
|
||||
DeviceCredentials createDevCredentials = new DeviceCredentials();
|
||||
createDevCredentials.setDeviceId(deviceId);
|
||||
createDevCredentials.setCredentialsType(credentialsType);
|
||||
createDevCredentials.setCredentialsId(certificateHash);
|
||||
createDevCredentials.setCredentialsValue(certificateValue);
|
||||
return deviceCredentialsService.createDeviceCredentials(tenantId, createDevCredentials);
|
||||
}
|
||||
|
||||
private Device createDevice(TenantId tenantId, DeviceProfileId deviceProfileId, String deviceName, String type) {
|
||||
log.trace("Creating new device for deviceProfile [{}] with device name [{}]", deviceProfileId, deviceName);
|
||||
Device device = new Device();
|
||||
device.setTenantId(tenantId);
|
||||
device.setDeviceProfileId(deviceProfileId);
|
||||
device.setName(deviceName);
|
||||
device.setType(type);
|
||||
device = deviceService.saveDevice(device);
|
||||
tbClusterService.onDeviceUpdated(device, null);
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,7 +735,7 @@ transport:
|
||||
# MQTT SSL configuration
|
||||
ssl:
|
||||
# Enable/disable SSL support
|
||||
enabled: "${MQTT_SSL_ENABLED:false}"
|
||||
enabled: "${MQTT_SSL_ENABLED:true}"
|
||||
# MQTT SSL bind address
|
||||
bind_address: "${MQTT_SSL_BIND_ADDRESS:0.0.0.0}"
|
||||
# MQTT SSL bind port
|
||||
@ -749,11 +749,11 @@ transport:
|
||||
# PEM server credentials
|
||||
pem:
|
||||
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
|
||||
cert_file: "${MQTT_SSL_PEM_CERT:mqttserver.pem}"
|
||||
cert_file: "${MQTT_SSL_PEM_CERT:/home/developer/x509/server.pem}"
|
||||
# Path to the server certificate private key file. Optional by default. Required if the private key is not present in server certificate file;
|
||||
key_file: "${MQTT_SSL_PEM_KEY:mqttserver_key.pem}"
|
||||
key_file: "${MQTT_SSL_PEM_KEY:/home/developer/x509/server_key.pem}"
|
||||
# Server certificate private key password (optional)
|
||||
key_password: "${MQTT_SSL_PEM_KEY_PASSWORD:server_key_password}"
|
||||
key_password: "${MQTT_SSL_PEM_KEY_PASSWORD:}"
|
||||
# Keystore server credentials
|
||||
keystore:
|
||||
# Type of the key store (JKS or PKCS12)
|
||||
|
||||
@ -302,14 +302,17 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
|
||||
@Test
|
||||
public void testSaveDeviceProfileWithSameCertificateHash() throws Exception {
|
||||
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
|
||||
deviceProfile.setCertificateHash("Certificate Hash");
|
||||
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
|
||||
deviceProfile.setProvisionDeviceKey("Certificate hash");
|
||||
|
||||
doPost("/api/deviceProfile", deviceProfile)
|
||||
.andExpect(status().isOk());
|
||||
|
||||
DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
|
||||
deviceProfile2.setCertificateHash("Certificate Hash");
|
||||
deviceProfile2.setProvisionDeviceKey("Certificate hash");
|
||||
|
||||
Mockito.reset(tbClusterService, auditLogService);
|
||||
|
||||
String msgError = "Device profile with such certificate hash already exists";
|
||||
String msgError = "Device profile with such provision device key already exists!";
|
||||
doPost("/api/deviceProfile", deviceProfile2)
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(statusReason(containsString(msgError)));
|
||||
|
||||
@ -40,6 +40,8 @@ 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.DeviceService;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
|
||||
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
|
||||
import org.thingsboard.server.dao.ota.OtaPackageService;
|
||||
import org.thingsboard.server.dao.queue.QueueService;
|
||||
import org.thingsboard.server.dao.relation.RelationService;
|
||||
@ -130,43 +132,42 @@ public class DefaultTransportApiServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExistingDeviceX509Certificate() {
|
||||
public void provisionDeviceX509Certificate() {
|
||||
var deviceProfile = createDeviceProfile(chain[1]);
|
||||
when(deviceProfileService.findDeviceProfileByCertificateHash(any())).thenReturn(deviceProfile);
|
||||
when(deviceProfileService.findDeviceProfileByProvisionDeviceKey(any())).thenReturn(deviceProfile);
|
||||
|
||||
var device = createDevice();
|
||||
when(deviceService.findDeviceByTenantIdAndName(any(), any())).thenReturn(device);
|
||||
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
|
||||
|
||||
var deviceCredentials = createDeviceCredentials(chain[0], device.getId());
|
||||
when(deviceCredentialsService.findDeviceCredentialsByDeviceId(any(), any())).thenReturn(deviceCredentials);
|
||||
when(deviceCredentialsService.findDeviceCredentialsByCredentialsId(any())).thenReturn(null);
|
||||
when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials);
|
||||
|
||||
var provisionResponse = createProvisionResponse(deviceCredentials);
|
||||
when(deviceProvisionService.provisionDevice(any())).thenReturn(provisionResponse);
|
||||
|
||||
service.validateOrCreateDeviceX509Certificate(certificateChain);
|
||||
verify(deviceProfileService, times(1)).findDeviceProfileByCertificateHash(any());
|
||||
verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any());
|
||||
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any());
|
||||
verify(deviceCredentialsService, times(1)).updateDeviceCredentials(any(), any());
|
||||
verify(deviceProfileService, times(1)).findDeviceProfileByProvisionDeviceKey(any());
|
||||
verify(deviceService, times(1)).findDeviceByIdAsync(any(), any());
|
||||
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByCredentialsId(any());
|
||||
verify(deviceProvisionService, times(1)).provisionDevice(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDeviceByX509Provision() {
|
||||
var deviceProfile = createDeviceProfile(chain[1]);
|
||||
when(deviceProfileService.findDeviceProfileByCertificateHash(any())).thenReturn(deviceProfile);
|
||||
private DeviceProfile createDeviceProfile(String certificateValue) {
|
||||
X509CertificateChainProvisionConfiguration provision = new X509CertificateChainProvisionConfiguration();
|
||||
provision.setProvisionDeviceSecret(certificateValue);
|
||||
provision.setCertificateRegExPattern("([^@]+)");
|
||||
provision.setAllowCreateNewDevicesByX509Certificate(true);
|
||||
|
||||
var device = createDevice();
|
||||
when(deviceService.saveDevice(any())).thenReturn(device);
|
||||
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
|
||||
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||
deviceProfileData.setProvisionConfiguration(provision);
|
||||
|
||||
var deviceCredentials = createDeviceCredentials(chain[0], device.getId());
|
||||
when(deviceCredentialsService.findDeviceCredentialsByDeviceId(any(), any())).thenReturn(deviceCredentials);
|
||||
when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials);
|
||||
|
||||
service.validateOrCreateDeviceX509Certificate(certificateChain);
|
||||
verify(deviceProfileService, times(1)).findDeviceProfileByCertificateHash(any());
|
||||
verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any());
|
||||
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any());
|
||||
verify(deviceCredentialsService, times(1)).updateDeviceCredentials(any(), any());
|
||||
DeviceProfile deviceProfile = new DeviceProfile();
|
||||
deviceProfile.setProfileData(deviceProfileData);
|
||||
deviceProfile.setProvisionDeviceKey(EncryptionUtil.getSha3Hash(certificateValue));
|
||||
deviceProfile.setProvisionType(DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN);
|
||||
return deviceProfile;
|
||||
}
|
||||
|
||||
private DeviceCredentials createDeviceCredentials(String certificateValue, DeviceId deviceId) {
|
||||
@ -178,26 +179,16 @@ public class DefaultTransportApiServiceTest {
|
||||
return deviceCredentials;
|
||||
}
|
||||
|
||||
private DeviceProfile createDeviceProfile(String certificateValue) {
|
||||
DeviceProfile deviceProfile = new DeviceProfile();
|
||||
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||
X509CertificateChainProvisionConfiguration provision = new X509CertificateChainProvisionConfiguration();
|
||||
provision.setCertificateValue(certificateValue);
|
||||
provision.setCertificateRegExPattern("([^@]+)");
|
||||
provision.setAllowCreateNewDevicesByX509Certificate(true);
|
||||
deviceProfileData.setProvisionConfiguration(provision);
|
||||
deviceProfile.setProfileData(deviceProfileData);
|
||||
deviceProfile.setCertificateHash(EncryptionUtil.getSha3Hash(certificateValue));
|
||||
deviceProfile.setProvisionType(DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN);
|
||||
return deviceProfile;
|
||||
}
|
||||
|
||||
private Device createDevice() {
|
||||
Device device = new Device();
|
||||
device.setId(new DeviceId(UUID.randomUUID()));
|
||||
return device;
|
||||
}
|
||||
|
||||
private ProvisionResponse createProvisionResponse(DeviceCredentials deviceCredentials) {
|
||||
return new ProvisionResponse(deviceCredentials, ProvisionResponseStatus.SUCCESS);
|
||||
}
|
||||
|
||||
public static String certTrimNewLinesForChainInDeviceProfile(String input) {
|
||||
return input.replaceAll("\n", "")
|
||||
.replaceAll("\r", "")
|
||||
|
||||
@ -39,7 +39,7 @@ public interface DeviceProfileService extends EntityDaoService {
|
||||
|
||||
PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType);
|
||||
|
||||
DeviceProfile findDeviceProfileByCertificateHash(String credentialsId);
|
||||
DeviceProfile findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey);
|
||||
|
||||
DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName);
|
||||
|
||||
|
||||
@ -66,10 +66,6 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
|
||||
private DeviceTransportType transportType;
|
||||
@ApiModelProperty(position = 15, value = "Provisioning strategy.")
|
||||
private DeviceProfileProvisionType provisionType;
|
||||
@ApiModelProperty(position = 18, value = "CA certificate hash. ")
|
||||
private String certificateHash;
|
||||
|
||||
|
||||
@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. " +
|
||||
"Otherwise, the root rule chain will be used to process those messages.")
|
||||
@ -125,7 +121,6 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
|
||||
this.firmwareId = deviceProfile.getFirmwareId();
|
||||
this.softwareId = deviceProfile.getSoftwareId();
|
||||
this.defaultEdgeRuleChainId = deviceProfile.getDefaultEdgeRuleChainId();
|
||||
this.certificateHash = deviceProfile.getCertificateHash();
|
||||
this.externalId = deviceProfile.getExternalId();
|
||||
}
|
||||
|
||||
|
||||
@ -24,16 +24,10 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType;
|
||||
@NoArgsConstructor
|
||||
public class X509CertificateChainProvisionConfiguration implements DeviceProfileProvisionConfiguration {
|
||||
|
||||
private String certificateValue;
|
||||
private String provisionDeviceSecret;
|
||||
private String certificateRegExPattern;
|
||||
private boolean allowCreateNewDevicesByX509Certificate;
|
||||
|
||||
@Override
|
||||
public String getProvisionDeviceSecret() {
|
||||
// ignore device secret for this strategy
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceProfileProvisionType getType() {
|
||||
return DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN;
|
||||
|
||||
@ -164,12 +164,10 @@ public class MqttSslHandlerProvider {
|
||||
}
|
||||
});
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
if (chain.length == 1) {
|
||||
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
|
||||
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
|
||||
if (chain.length == 1) {
|
||||
throw new CertificateException("Invalid Device Certificate");
|
||||
}
|
||||
} else {
|
||||
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
|
||||
} else {
|
||||
throw new CertificateException("Invalid Chain of X509 Certificates");
|
||||
}
|
||||
}
|
||||
@ -177,6 +175,5 @@ public class MqttSslHandlerProvider {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package org.thingsboard.server.common.transport.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.bouncycastle.asn1.x500.RDN;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
@ -24,8 +25,11 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.thingsboard.server.common.msg.EncryptionUtil;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
@ -53,6 +57,23 @@ public class SslUtil {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static X509Certificate readCertFile(String fileContent) {
|
||||
X509Certificate certificate = null;
|
||||
try {
|
||||
if (fileContent != null && !fileContent.trim().isEmpty()) {
|
||||
fileContent = fileContent.replace("-----BEGIN CERTIFICATE-----", "")
|
||||
.replace("-----END CERTIFICATE-----", "")
|
||||
.replaceAll("\\s", "");
|
||||
byte[] decoded = Base64.decodeBase64(fileContent);
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
try (InputStream inStream = new ByteArrayInputStream(decoded)) {
|
||||
certificate = (X509Certificate) certFactory.generateCertificate(inStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public static String parseCommonName(X509Certificate certificate) {
|
||||
X500Name x500name;
|
||||
try {
|
||||
|
||||
@ -29,14 +29,14 @@ public class DeviceProfileCacheKey implements Serializable {
|
||||
private final TenantId tenantId;
|
||||
private final String name;
|
||||
private final DeviceProfileId deviceProfileId;
|
||||
private final String certificateHash;
|
||||
private final String provisionDeviceKey;
|
||||
private final boolean defaultProfile;
|
||||
|
||||
private DeviceProfileCacheKey(TenantId tenantId, String name, DeviceProfileId deviceProfileId, String certificateHash, boolean defaultProfile) {
|
||||
private DeviceProfileCacheKey(TenantId tenantId, String name, DeviceProfileId deviceProfileId, String provisionDeviceKey, boolean defaultProfile) {
|
||||
this.tenantId = tenantId;
|
||||
this.name = name;
|
||||
this.deviceProfileId = deviceProfileId;
|
||||
this.certificateHash = certificateHash;
|
||||
this.provisionDeviceKey = provisionDeviceKey;
|
||||
this.defaultProfile = defaultProfile;
|
||||
}
|
||||
|
||||
@ -48,8 +48,8 @@ public class DeviceProfileCacheKey implements Serializable {
|
||||
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 fromProvisionDeviceKey(String provisionDeviceKey) {
|
||||
return new DeviceProfileCacheKey(null, null, null, provisionDeviceKey, false);
|
||||
}
|
||||
|
||||
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
|
||||
@ -65,8 +65,8 @@ public class DeviceProfileCacheKey implements Serializable {
|
||||
return deviceProfileId.toString();
|
||||
} else if (defaultProfile) {
|
||||
return tenantId.toString();
|
||||
} else if (certificateHash != null) {
|
||||
return certificateHash;
|
||||
} else if (provisionDeviceKey != null) {
|
||||
return provisionDeviceKey;
|
||||
}
|
||||
return tenantId + "_" + name;
|
||||
}
|
||||
|
||||
@ -46,5 +46,4 @@ public interface DeviceProfileDao extends Dao<DeviceProfile>, ExportableEntityDa
|
||||
|
||||
DeviceProfile findByName(TenantId tenantId, String profileName);
|
||||
|
||||
DeviceProfile findByCertificateHash(String certificateHash);
|
||||
}
|
||||
|
||||
@ -27,6 +27,6 @@ public class DeviceProfileEvictEvent {
|
||||
private final String oldName;
|
||||
private final DeviceProfileId deviceProfileId;
|
||||
private final boolean defaultProfile;
|
||||
private final String certificateHash;
|
||||
private final String provisionDeviceKey;
|
||||
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
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_NAME = "Incorrect deviceProfileName ";
|
||||
private static final String INCORRECT_DEVICE_PROFILE_CREDENTIALS_HASH = "Incorrect deviceProfileCertificateHash ";
|
||||
private static final String INCORRECT_PROVISION_DEVICE_KEY = "Incorrect provisionDeviceKey ";
|
||||
private static final String DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS = "Device profile with such name already exists!";
|
||||
|
||||
@Autowired
|
||||
@ -103,8 +103,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
|
||||
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
|
||||
}
|
||||
if (event.getCertificateHash() != null) {
|
||||
keys.add(DeviceProfileCacheKey.fromCertificateHash(event.getCertificateHash()));
|
||||
if (event.getProvisionDeviceKey() != null) {
|
||||
keys.add(DeviceProfileCacheKey.fromProvisionDeviceKey(event.getProvisionDeviceKey()));
|
||||
}
|
||||
cache.evict(keys);
|
||||
}
|
||||
@ -112,7 +112,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
@Override
|
||||
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
|
||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromId(deviceProfileId),
|
||||
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true);
|
||||
}
|
||||
@ -120,7 +120,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
@Override
|
||||
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) {
|
||||
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName);
|
||||
Validator.validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName);
|
||||
validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName);
|
||||
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromName(tenantId, profileName),
|
||||
() -> deviceProfileDao.findByName(tenantId, profileName), true);
|
||||
}
|
||||
@ -128,7 +128,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
@Override
|
||||
public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
|
||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
return toDeviceProfileInfo(findDeviceProfileById(tenantId, deviceProfileId));
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
log.trace("Executing saveDeviceProfile [{}]", deviceProfile);
|
||||
if (deviceProfile.getProfileData() != null && deviceProfile.getProfileData().getProvisionConfiguration() instanceof X509CertificateChainProvisionConfiguration) {
|
||||
X509CertificateChainProvisionConfiguration x509Configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
|
||||
if (x509Configuration.getCertificateValue() != null) {
|
||||
if (x509Configuration.getProvisionDeviceSecret() != null) {
|
||||
formatDeviceProfileCertificate(deviceProfile, x509Configuration);
|
||||
}
|
||||
}
|
||||
@ -147,11 +147,11 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
|
||||
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
|
||||
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault(),
|
||||
deviceProfile.getCertificateHash() != null ? deviceProfile.getCertificateHash() : null));
|
||||
deviceProfile.getProvisionDeviceKey() != null ? deviceProfile.getProvisionDeviceKey() : null));
|
||||
} catch (Exception t) {
|
||||
handleEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
||||
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, null, deviceProfile.isDefault(),
|
||||
deviceProfile.getCertificateHash() != null ? deviceProfile.getCertificateHash() : null));
|
||||
deviceProfile.getProvisionDeviceKey() != null ? deviceProfile.getProvisionDeviceKey() : null));
|
||||
checkConstraintViolation(t,
|
||||
Map.of("device_profile_name_unq_key", DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS,
|
||||
"device_provision_key_unq_key", "Device profile with such provision device key already exists!",
|
||||
@ -177,7 +177,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
@Transactional
|
||||
public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||
log.trace("Executing deleteDeviceProfile [{}]", deviceProfileId);
|
||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
||||
if (deviceProfile != null && deviceProfile.isDefault()) {
|
||||
throw new DataValidationException("Deletion of Default Device Profile is prohibited!");
|
||||
@ -192,7 +192,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
deviceProfileDao.removeById(tenantId, deviceProfileId.getId());
|
||||
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
||||
null, deviceProfile.getId(), deviceProfile.isDefault(),
|
||||
deviceProfile.getCertificateHash() != null ? deviceProfile.getCertificateHash() : null));
|
||||
deviceProfile.getProvisionDeviceKey() != null ? deviceProfile.getProvisionDeviceKey() : null));
|
||||
} catch (Exception t) {
|
||||
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
||||
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
|
||||
@ -220,11 +220,11 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
}
|
||||
|
||||
@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);
|
||||
public DeviceProfile findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey) {
|
||||
log.trace("Executing findDeviceProfileIdByCredentialsId credentialId [{}]", provisionDeviceKey);
|
||||
validateString(provisionDeviceKey, INCORRECT_PROVISION_DEVICE_KEY + provisionDeviceKey);
|
||||
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey),
|
||||
() -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -290,7 +290,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
@Override
|
||||
public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||
log.trace("Executing setDefaultDeviceProfile [{}]", deviceProfileId);
|
||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
||||
if (!deviceProfile.isDefault()) {
|
||||
deviceProfile.setDefault(true);
|
||||
@ -350,14 +350,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
||||
}
|
||||
|
||||
private void formatDeviceProfileCertificate(DeviceProfile deviceProfile, X509CertificateChainProvisionConfiguration x509Configuration) {
|
||||
String formattedCertificateValue = formatCertificateValue(x509Configuration.getCertificateValue());
|
||||
String formattedCertificateValue = formatCertificateValue(x509Configuration.getProvisionDeviceSecret());
|
||||
String cert = fetchLeafCertificateFromChain(formattedCertificateValue);
|
||||
String sha3Hash = EncryptionUtil.getSha3Hash(cert);
|
||||
DeviceProfileData deviceProfileData = deviceProfile.getProfileData();
|
||||
x509Configuration.setCertificateValue(formattedCertificateValue);
|
||||
x509Configuration.setProvisionDeviceSecret(formattedCertificateValue);
|
||||
deviceProfileData.setProvisionConfiguration(x509Configuration);
|
||||
deviceProfile.setProfileData(deviceProfileData);
|
||||
deviceProfile.setCertificateHash(sha3Hash);
|
||||
deviceProfile.setProvisionDeviceKey(sha3Hash);
|
||||
}
|
||||
|
||||
private String fetchLeafCertificateFromChain(String value) {
|
||||
|
||||
@ -195,7 +195,6 @@ public class ModelConstants {
|
||||
public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
|
||||
public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
|
||||
public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";
|
||||
public static final String DEVICE_PROFILE_CERTIFICATE_HASH_PROPERTY = "certificate_hash";
|
||||
public static final String DEVICE_PROFILE_DEFAULT_EDGE_RULE_CHAIN_ID_PROPERTY = "default_edge_rule_chain_id";
|
||||
|
||||
/**
|
||||
|
||||
@ -109,9 +109,6 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
|
||||
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
|
||||
private UUID externalId;
|
||||
|
||||
@Column(name = ModelConstants.DEVICE_PROFILE_CERTIFICATE_HASH_PROPERTY)
|
||||
private String certificateHash;
|
||||
|
||||
public DeviceProfileEntity() {
|
||||
super();
|
||||
}
|
||||
@ -129,7 +126,6 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
|
||||
this.image = deviceProfile.getImage();
|
||||
this.transportType = deviceProfile.getTransportType();
|
||||
this.provisionType = deviceProfile.getProvisionType();
|
||||
this.certificateHash = deviceProfile.getCertificateHash();
|
||||
this.description = deviceProfile.getDescription();
|
||||
this.isDefault = deviceProfile.isDefault();
|
||||
this.profileData = JacksonUtil.convertValue(deviceProfile.getProfileData(), ObjectNode.class);
|
||||
@ -192,7 +188,6 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
|
||||
deviceProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId));
|
||||
}
|
||||
deviceProfile.setProvisionDeviceKey(provisionDeviceKey);
|
||||
deviceProfile.setCertificateHash(certificateHash);
|
||||
|
||||
if (firmwareId != null) {
|
||||
deviceProfile.setFirmwareId(new OtaPackageId(firmwareId));
|
||||
|
||||
@ -132,14 +132,14 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
||||
if (deviceProfile.getProvisionType() == null) {
|
||||
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
|
||||
}
|
||||
if (deviceProfile.getCertificateHash() != null) {
|
||||
if (getRootCAFromJavaCacerts(deviceProfile.getCertificateHash())) {
|
||||
if (deviceProfile.getProvisionDeviceKey() != null) {
|
||||
if (getRootCAFromJavaCacerts(deviceProfile.getProvisionDeviceKey())) {
|
||||
throw new DataValidationException("Device profile certificate cannot be well known root CA!");
|
||||
}
|
||||
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
||||
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(deviceProfile.getId())) {
|
||||
throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||
}
|
||||
// DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByProvisionDeviceKey(deviceProfile.getProvisionDeviceKey());
|
||||
// if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(deviceProfile.getId())) {
|
||||
// throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||
// }
|
||||
}
|
||||
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
|
||||
transportConfiguration.validate();
|
||||
@ -234,14 +234,14 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
||||
throw new DataValidationException(message);
|
||||
}
|
||||
}
|
||||
if (deviceProfile.getCertificateHash() != null) {
|
||||
if (getRootCAFromJavaCacerts(deviceProfile.getCertificateHash())) {
|
||||
if (deviceProfile.getProvisionDeviceKey() != null) {
|
||||
if (getRootCAFromJavaCacerts(deviceProfile.getProvisionDeviceKey())) {
|
||||
throw new DataValidationException("Device profile certificate cannot be well known root CA!");
|
||||
}
|
||||
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
||||
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(old.getId())) {
|
||||
throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||
}
|
||||
// DeviceProfile existingDeviceProfileWithDeviceKey = deviceProfileDao.findByProvisionDeviceKey(deviceProfile.getProvisionDeviceKey());
|
||||
// if (existingDeviceProfileWithDeviceKey != null && !existingDeviceProfileWithDeviceKey.getId().equals(old.getId())) {
|
||||
// throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||
// }
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
@ -64,8 +64,6 @@ public interface DeviceProfileRepository extends JpaRepository<DeviceProfileEnti
|
||||
"WHERE d.tenantId = :tenantId AND d.isDefault = true")
|
||||
DeviceProfileInfo findDefaultDeviceProfileInfo(@Param("tenantId") UUID tenantId);
|
||||
|
||||
DeviceProfileEntity findDeviceProfileByCertificateHash(String certificateHash);
|
||||
|
||||
DeviceProfileEntity findByTenantIdAndName(UUID id, String profileName);
|
||||
|
||||
DeviceProfileEntity findByProvisionDeviceKey(@Param("provisionDeviceKey") String provisionDeviceKey);
|
||||
|
||||
@ -115,11 +115,6 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE
|
||||
return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceProfile findByCertificateHash(String certificateHash) {
|
||||
return DaoUtil.getData(deviceProfileRepository.findDeviceProfileByCertificateHash(certificateHash));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) {
|
||||
return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndExternalId(tenantId, externalId));
|
||||
|
||||
@ -295,10 +295,8 @@ CREATE TABLE IF NOT EXISTS device_profile (
|
||||
default_dashboard_id uuid,
|
||||
default_queue_name varchar(255),
|
||||
provision_device_key varchar,
|
||||
certificate_hash varchar,
|
||||
default_edge_rule_chain_id uuid,
|
||||
external_id uuid,
|
||||
CONSTRAINT device_profile_credentials_hash_unq_key UNIQUE (certificate_hash),
|
||||
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
|
||||
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
|
||||
CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user