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
|
-- 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
|
-- TTL DROP PARTITIONS FUNCTIONS UPDATE START
|
||||||
|
|
||||||
DROP PROCEDURE IF EXISTS drop_partitions_by_max_ttl(character varying, bigint, bigint);
|
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.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.cluster.TbClusterService;
|
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.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.audit.ActionType;
|
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.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
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.BaseAttributeKvEntry;
|
||||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
||||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
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.TbMsg;
|
||||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
|
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.attributes.AttributesService;
|
||||||
import org.thingsboard.server.dao.audit.AuditLogService;
|
import org.thingsboard.server.dao.audit.AuditLogService;
|
||||||
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
||||||
import org.thingsboard.server.dao.device.DeviceDao;
|
import org.thingsboard.server.dao.device.DeviceDao;
|
||||||
import org.thingsboard.server.dao.device.DeviceProfileDao;
|
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.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;
|
||||||
@ -77,35 +81,23 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
|||||||
private static final String DEVICE_PROVISION_STATE = "provisionState";
|
private static final String DEVICE_PROVISION_STATE = "provisionState";
|
||||||
private static final String PROVISIONED_STATE = "provisioned";
|
private static final String PROVISIONED_STATE = "provisioned";
|
||||||
|
|
||||||
@Autowired
|
private final TbClusterService clusterService;
|
||||||
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
|
public DeviceProvisionServiceImpl(TbQueueProducerProvider producerProvider, TbClusterService clusterService, DeviceDao deviceDao, DeviceProfileDao deviceProfileDao, DeviceProfileService deviceProfileService, DeviceService deviceService, DeviceCredentialsService deviceCredentialsService, AttributesService attributesService, DeviceStateService deviceStateService, AuditLogService auditLogService, PartitionService partitionService) {
|
||||||
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) {
|
|
||||||
ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer();
|
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
|
@Override
|
||||||
@ -124,14 +116,14 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
|||||||
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceProfile targetProfile = deviceProfileDao.findByProvisionDeviceKey(provisionRequestKey);
|
DeviceProfile targetProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(provisionRequestKey);
|
||||||
|
|
||||||
if (targetProfile == null || targetProfile.getProfileData().getProvisionConfiguration() == null ||
|
if (targetProfile == null || targetProfile.getProfileData().getProvisionConfiguration() == null ||
|
||||||
targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() == null) {
|
targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() == null) {
|
||||||
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
|
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()) {
|
switch (targetProfile.getProvisionType()) {
|
||||||
case ALLOW_CREATE_NEW_DEVICES:
|
case ALLOW_CREATE_NEW_DEVICES:
|
||||||
@ -155,6 +147,22 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
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());
|
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) {
|
private ListenableFuture<List<String>> saveProvisionStateAttribute(Device device) {
|
||||||
return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
|
return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
|
||||||
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE),
|
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 com.google.protobuf.ByteString;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
|
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.DataConstants;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
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.DeviceTransportType;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.OtaPackage;
|
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.Lwm2mDeviceTransportConfiguration;
|
||||||
import org.thingsboard.server.common.data.device.data.PowerMode;
|
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.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.ProvisionDeviceProfileCredentials;
|
||||||
import org.thingsboard.server.common.data.device.profile.X509CertificateChainProvisionConfiguration;
|
import org.thingsboard.server.common.data.device.profile.X509CertificateChainProvisionConfiguration;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
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.ProvisionFailedException;
|
||||||
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
|
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
|
||||||
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
|
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.ota.OtaPackageService;
|
||||||
import org.thingsboard.server.dao.queue.QueueService;
|
import org.thingsboard.server.dao.queue.QueueService;
|
||||||
import org.thingsboard.server.dao.relation.RelationService;
|
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.profile.TbDeviceProfileCache;
|
||||||
import org.thingsboard.server.service.resource.TbResourceService;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -250,38 +246,13 @@ public class DefaultTransportApiService implements TransportApiService {
|
|||||||
if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
|
if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
|
||||||
return getDeviceInfo(credentials);
|
return getDeviceInfo(credentials);
|
||||||
}
|
}
|
||||||
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(certificateHash);
|
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(certificateHash);
|
||||||
if (deviceProfile != null) {
|
if (deviceProfile != null) {
|
||||||
X509CertificateChainProvisionConfiguration x509Configuration;
|
String updatedDeviceProvisionSecret = chain.get(0);
|
||||||
if (deviceProfile.getProfileData().getProvisionConfiguration() instanceof X509CertificateChainProvisionConfiguration) {
|
ProvisionRequest provisionRequest = createProvisionRequest(deviceProfile, updatedDeviceProvisionSecret);
|
||||||
x509Configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
|
ProvisionResponse provisionResponse = deviceProvisionService.provisionDevice(provisionRequest);
|
||||||
} else {
|
if (provisionResponse.getResponseStatus().equals(ProvisionResponseStatus.SUCCESS)) {
|
||||||
log.warn("Device Profile provision configuration is not X509CertificateChainProvisionConfiguration");
|
return getDeviceInfo(provisionResponse.getDeviceCredentials());
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -728,9 +699,37 @@ public class DefaultTransportApiService implements TransportApiService {
|
|||||||
return l != null ? l : 0;
|
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) {
|
private String extractDeviceNameFromCertificateCNByRegEx(String x509Value, String regex) {
|
||||||
try {
|
try {
|
||||||
String commonName = SslUtil.parseCommonName(readCertFile(x509Value));
|
String commonName = SslUtil.parseCommonName(SslUtil.readCertFile(x509Value));
|
||||||
log.trace("Extract CN [{}] by regex pattern [{}]", commonName, regex);
|
log.trace("Extract CN [{}] by regex pattern [{}]", commonName, regex);
|
||||||
Pattern pattern = Pattern.compile(regex);
|
Pattern pattern = Pattern.compile(regex);
|
||||||
Matcher matcher = pattern.matcher(commonName);
|
Matcher matcher = pattern.matcher(commonName);
|
||||||
@ -751,53 +750,4 @@ public class DefaultTransportApiService implements TransportApiService {
|
|||||||
}
|
}
|
||||||
return chain;
|
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
|
# MQTT SSL configuration
|
||||||
ssl:
|
ssl:
|
||||||
# Enable/disable SSL support
|
# Enable/disable SSL support
|
||||||
enabled: "${MQTT_SSL_ENABLED:false}"
|
enabled: "${MQTT_SSL_ENABLED:true}"
|
||||||
# MQTT SSL bind address
|
# MQTT SSL bind address
|
||||||
bind_address: "${MQTT_SSL_BIND_ADDRESS:0.0.0.0}"
|
bind_address: "${MQTT_SSL_BIND_ADDRESS:0.0.0.0}"
|
||||||
# MQTT SSL bind port
|
# MQTT SSL bind port
|
||||||
@ -749,11 +749,11 @@ transport:
|
|||||||
# PEM server credentials
|
# PEM server credentials
|
||||||
pem:
|
pem:
|
||||||
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
|
# 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;
|
# 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)
|
# 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 server credentials
|
||||||
keystore:
|
keystore:
|
||||||
# Type of the key store (JKS or PKCS12)
|
# Type of the key store (JKS or PKCS12)
|
||||||
|
|||||||
@ -302,14 +302,17 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
|
|||||||
@Test
|
@Test
|
||||||
public void testSaveDeviceProfileWithSameCertificateHash() throws Exception {
|
public void testSaveDeviceProfileWithSameCertificateHash() throws Exception {
|
||||||
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
|
||||||
deviceProfile.setCertificateHash("Certificate Hash");
|
deviceProfile.setProvisionDeviceKey("Certificate hash");
|
||||||
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
|
|
||||||
|
doPost("/api/deviceProfile", deviceProfile)
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
|
DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
|
||||||
deviceProfile2.setCertificateHash("Certificate Hash");
|
deviceProfile2.setProvisionDeviceKey("Certificate hash");
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
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)
|
doPost("/api/deviceProfile", deviceProfile2)
|
||||||
.andExpect(status().isBadRequest())
|
.andExpect(status().isBadRequest())
|
||||||
.andExpect(statusReason(containsString(msgError)));
|
.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.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.ProvisionResponse;
|
||||||
|
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
|
||||||
import org.thingsboard.server.dao.ota.OtaPackageService;
|
import org.thingsboard.server.dao.ota.OtaPackageService;
|
||||||
import org.thingsboard.server.dao.queue.QueueService;
|
import org.thingsboard.server.dao.queue.QueueService;
|
||||||
import org.thingsboard.server.dao.relation.RelationService;
|
import org.thingsboard.server.dao.relation.RelationService;
|
||||||
@ -130,43 +132,42 @@ public class DefaultTransportApiServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateExistingDeviceX509Certificate() {
|
public void provisionDeviceX509Certificate() {
|
||||||
var deviceProfile = createDeviceProfile(chain[1]);
|
var deviceProfile = createDeviceProfile(chain[1]);
|
||||||
when(deviceProfileService.findDeviceProfileByCertificateHash(any())).thenReturn(deviceProfile);
|
when(deviceProfileService.findDeviceProfileByProvisionDeviceKey(any())).thenReturn(deviceProfile);
|
||||||
|
|
||||||
var device = createDevice();
|
var device = createDevice();
|
||||||
when(deviceService.findDeviceByTenantIdAndName(any(), any())).thenReturn(device);
|
when(deviceService.findDeviceByTenantIdAndName(any(), any())).thenReturn(device);
|
||||||
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
|
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
|
||||||
|
|
||||||
var deviceCredentials = createDeviceCredentials(chain[0], device.getId());
|
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);
|
when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials);
|
||||||
|
|
||||||
|
var provisionResponse = createProvisionResponse(deviceCredentials);
|
||||||
|
when(deviceProvisionService.provisionDevice(any())).thenReturn(provisionResponse);
|
||||||
|
|
||||||
service.validateOrCreateDeviceX509Certificate(certificateChain);
|
service.validateOrCreateDeviceX509Certificate(certificateChain);
|
||||||
verify(deviceProfileService, times(1)).findDeviceProfileByCertificateHash(any());
|
verify(deviceProfileService, times(1)).findDeviceProfileByProvisionDeviceKey(any());
|
||||||
verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any());
|
verify(deviceService, times(1)).findDeviceByIdAsync(any(), any());
|
||||||
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any());
|
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByCredentialsId(any());
|
||||||
verify(deviceCredentialsService, times(1)).updateDeviceCredentials(any(), any());
|
verify(deviceProvisionService, times(1)).provisionDevice(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private DeviceProfile createDeviceProfile(String certificateValue) {
|
||||||
public void createDeviceByX509Provision() {
|
X509CertificateChainProvisionConfiguration provision = new X509CertificateChainProvisionConfiguration();
|
||||||
var deviceProfile = createDeviceProfile(chain[1]);
|
provision.setProvisionDeviceSecret(certificateValue);
|
||||||
when(deviceProfileService.findDeviceProfileByCertificateHash(any())).thenReturn(deviceProfile);
|
provision.setCertificateRegExPattern("([^@]+)");
|
||||||
|
provision.setAllowCreateNewDevicesByX509Certificate(true);
|
||||||
|
|
||||||
var device = createDevice();
|
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||||
when(deviceService.saveDevice(any())).thenReturn(device);
|
deviceProfileData.setProvisionConfiguration(provision);
|
||||||
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
|
|
||||||
|
|
||||||
var deviceCredentials = createDeviceCredentials(chain[0], device.getId());
|
DeviceProfile deviceProfile = new DeviceProfile();
|
||||||
when(deviceCredentialsService.findDeviceCredentialsByDeviceId(any(), any())).thenReturn(deviceCredentials);
|
deviceProfile.setProfileData(deviceProfileData);
|
||||||
when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials);
|
deviceProfile.setProvisionDeviceKey(EncryptionUtil.getSha3Hash(certificateValue));
|
||||||
|
deviceProfile.setProvisionType(DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN);
|
||||||
service.validateOrCreateDeviceX509Certificate(certificateChain);
|
return deviceProfile;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DeviceCredentials createDeviceCredentials(String certificateValue, DeviceId deviceId) {
|
private DeviceCredentials createDeviceCredentials(String certificateValue, DeviceId deviceId) {
|
||||||
@ -178,26 +179,16 @@ public class DefaultTransportApiServiceTest {
|
|||||||
return deviceCredentials;
|
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() {
|
private Device createDevice() {
|
||||||
Device device = new Device();
|
Device device = new Device();
|
||||||
device.setId(new DeviceId(UUID.randomUUID()));
|
device.setId(new DeviceId(UUID.randomUUID()));
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProvisionResponse createProvisionResponse(DeviceCredentials deviceCredentials) {
|
||||||
|
return new ProvisionResponse(deviceCredentials, ProvisionResponseStatus.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
public static String certTrimNewLinesForChainInDeviceProfile(String input) {
|
public static String certTrimNewLinesForChainInDeviceProfile(String input) {
|
||||||
return input.replaceAll("\n", "")
|
return input.replaceAll("\n", "")
|
||||||
.replaceAll("\r", "")
|
.replaceAll("\r", "")
|
||||||
|
|||||||
@ -39,7 +39,7 @@ 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 findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey);
|
||||||
|
|
||||||
DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName);
|
DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName);
|
||||||
|
|
||||||
|
|||||||
@ -66,10 +66,6 @@ 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 = 18, value = "CA certificate hash. ")
|
|
||||||
private String certificateHash;
|
|
||||||
|
|
||||||
|
|
||||||
@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.")
|
||||||
@ -125,7 +121,6 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
|
|||||||
this.firmwareId = deviceProfile.getFirmwareId();
|
this.firmwareId = deviceProfile.getFirmwareId();
|
||||||
this.softwareId = deviceProfile.getSoftwareId();
|
this.softwareId = deviceProfile.getSoftwareId();
|
||||||
this.defaultEdgeRuleChainId = deviceProfile.getDefaultEdgeRuleChainId();
|
this.defaultEdgeRuleChainId = deviceProfile.getDefaultEdgeRuleChainId();
|
||||||
this.certificateHash = deviceProfile.getCertificateHash();
|
|
||||||
this.externalId = deviceProfile.getExternalId();
|
this.externalId = deviceProfile.getExternalId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,16 +24,10 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class X509CertificateChainProvisionConfiguration implements DeviceProfileProvisionConfiguration {
|
public class X509CertificateChainProvisionConfiguration implements DeviceProfileProvisionConfiguration {
|
||||||
|
|
||||||
private String certificateValue;
|
private String provisionDeviceSecret;
|
||||||
private String certificateRegExPattern;
|
private String certificateRegExPattern;
|
||||||
private boolean allowCreateNewDevicesByX509Certificate;
|
private boolean allowCreateNewDevicesByX509Certificate;
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getProvisionDeviceSecret() {
|
|
||||||
// ignore device secret for this strategy
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfileProvisionType getType() {
|
public DeviceProfileProvisionType getType() {
|
||||||
return DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN;
|
return DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN;
|
||||||
|
|||||||
@ -164,12 +164,10 @@ public class MqttSslHandlerProvider {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
latch.await(10, TimeUnit.SECONDS);
|
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");
|
throw new CertificateException("Invalid Device Certificate");
|
||||||
}
|
} else {
|
||||||
} else {
|
|
||||||
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
|
|
||||||
throw new CertificateException("Invalid Chain of X509 Certificates");
|
throw new CertificateException("Invalid Chain of X509 Certificates");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,6 +175,5 @@ public class MqttSslHandlerProvider {
|
|||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
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.apache.commons.codec.binary.Base64;
|
||||||
import org.bouncycastle.asn1.x500.RDN;
|
import org.bouncycastle.asn1.x500.RDN;
|
||||||
import org.bouncycastle.asn1.x500.X500Name;
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||||
@ -24,8 +25,11 @@ 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.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +57,23 @@ public class SslUtil {
|
|||||||
return stringBuilder.toString();
|
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) {
|
public static String parseCommonName(X509Certificate certificate) {
|
||||||
X500Name x500name;
|
X500Name x500name;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -29,14 +29,14 @@ 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 String provisionDeviceKey;
|
||||||
private final boolean defaultProfile;
|
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.tenantId = tenantId;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.deviceProfileId = deviceProfileId;
|
this.deviceProfileId = deviceProfileId;
|
||||||
this.certificateHash = certificateHash;
|
this.provisionDeviceKey = provisionDeviceKey;
|
||||||
this.defaultProfile = defaultProfile;
|
this.defaultProfile = defaultProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ public class DeviceProfileCacheKey implements Serializable {
|
|||||||
return new DeviceProfileCacheKey(null, null, id, null, false);
|
return new DeviceProfileCacheKey(null, null, id, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DeviceProfileCacheKey fromCertificateHash(String certificateHash) {
|
public static DeviceProfileCacheKey fromProvisionDeviceKey(String provisionDeviceKey) {
|
||||||
return new DeviceProfileCacheKey(null, null, null, certificateHash, false);
|
return new DeviceProfileCacheKey(null, null, null, provisionDeviceKey, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
|
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
|
||||||
@ -65,8 +65,8 @@ public class DeviceProfileCacheKey implements Serializable {
|
|||||||
return deviceProfileId.toString();
|
return deviceProfileId.toString();
|
||||||
} else if (defaultProfile) {
|
} else if (defaultProfile) {
|
||||||
return tenantId.toString();
|
return tenantId.toString();
|
||||||
} else if (certificateHash != null) {
|
} else if (provisionDeviceKey != null) {
|
||||||
return certificateHash;
|
return provisionDeviceKey;
|
||||||
}
|
}
|
||||||
return tenantId + "_" + name;
|
return tenantId + "_" + name;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,5 +46,4 @@ public interface DeviceProfileDao extends Dao<DeviceProfile>, ExportableEntityDa
|
|||||||
|
|
||||||
DeviceProfile findByName(TenantId tenantId, String profileName);
|
DeviceProfile findByName(TenantId tenantId, String profileName);
|
||||||
|
|
||||||
DeviceProfile findByCertificateHash(String certificateHash);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,6 @@ public class DeviceProfileEvictEvent {
|
|||||||
private final String oldName;
|
private final String oldName;
|
||||||
private final DeviceProfileId deviceProfileId;
|
private final DeviceProfileId deviceProfileId;
|
||||||
private final boolean defaultProfile;
|
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_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 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!";
|
private static final String DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS = "Device profile with such name already exists!";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -103,8 +103,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
|
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
|
||||||
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
|
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
|
||||||
}
|
}
|
||||||
if (event.getCertificateHash() != null) {
|
if (event.getProvisionDeviceKey() != null) {
|
||||||
keys.add(DeviceProfileCacheKey.fromCertificateHash(event.getCertificateHash()));
|
keys.add(DeviceProfileCacheKey.fromProvisionDeviceKey(event.getProvisionDeviceKey()));
|
||||||
}
|
}
|
||||||
cache.evict(keys);
|
cache.evict(keys);
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
@Override
|
@Override
|
||||||
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing findDeviceProfileById [{}]", 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),
|
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromId(deviceProfileId),
|
||||||
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true);
|
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true);
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
@Override
|
@Override
|
||||||
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) {
|
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) {
|
||||||
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, 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),
|
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromName(tenantId, profileName),
|
||||||
() -> deviceProfileDao.findByName(tenantId, profileName), true);
|
() -> deviceProfileDao.findByName(tenantId, profileName), true);
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
@Override
|
@Override
|
||||||
public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing findDeviceProfileById [{}]", 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));
|
return toDeviceProfileInfo(findDeviceProfileById(tenantId, deviceProfileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
log.trace("Executing saveDeviceProfile [{}]", deviceProfile);
|
log.trace("Executing saveDeviceProfile [{}]", deviceProfile);
|
||||||
if (deviceProfile.getProfileData() != null && deviceProfile.getProfileData().getProvisionConfiguration() instanceof X509CertificateChainProvisionConfiguration) {
|
if (deviceProfile.getProfileData() != null && deviceProfile.getProfileData().getProvisionConfiguration() instanceof X509CertificateChainProvisionConfiguration) {
|
||||||
X509CertificateChainProvisionConfiguration x509Configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
|
X509CertificateChainProvisionConfiguration x509Configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
|
||||||
if (x509Configuration.getCertificateValue() != null) {
|
if (x509Configuration.getProvisionDeviceSecret() != null) {
|
||||||
formatDeviceProfileCertificate(deviceProfile, x509Configuration);
|
formatDeviceProfileCertificate(deviceProfile, x509Configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,11 +147,11 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
|
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
|
||||||
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
|
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
|
||||||
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault(),
|
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault(),
|
||||||
deviceProfile.getCertificateHash() != null ? deviceProfile.getCertificateHash() : null));
|
deviceProfile.getProvisionDeviceKey() != null ? deviceProfile.getProvisionDeviceKey() : null));
|
||||||
} catch (Exception t) {
|
} catch (Exception t) {
|
||||||
handleEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
handleEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
||||||
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, null, deviceProfile.isDefault(),
|
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, null, deviceProfile.isDefault(),
|
||||||
deviceProfile.getCertificateHash() != null ? deviceProfile.getCertificateHash() : null));
|
deviceProfile.getProvisionDeviceKey() != null ? deviceProfile.getProvisionDeviceKey() : null));
|
||||||
checkConstraintViolation(t,
|
checkConstraintViolation(t,
|
||||||
Map.of("device_profile_name_unq_key", DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS,
|
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!",
|
"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
|
@Transactional
|
||||||
public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing deleteDeviceProfile [{}]", 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());
|
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
||||||
if (deviceProfile != null && deviceProfile.isDefault()) {
|
if (deviceProfile != null && deviceProfile.isDefault()) {
|
||||||
throw new DataValidationException("Deletion of Default Device Profile is prohibited!");
|
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());
|
deviceProfileDao.removeById(tenantId, deviceProfileId.getId());
|
||||||
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
||||||
null, deviceProfile.getId(), deviceProfile.isDefault(),
|
null, deviceProfile.getId(), deviceProfile.isDefault(),
|
||||||
deviceProfile.getCertificateHash() != null ? deviceProfile.getCertificateHash() : null));
|
deviceProfile.getProvisionDeviceKey() != null ? deviceProfile.getProvisionDeviceKey() : null));
|
||||||
} catch (Exception t) {
|
} catch (Exception t) {
|
||||||
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
||||||
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
|
||||||
@ -220,11 +220,11 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile findDeviceProfileByCertificateHash(String certificateHash) {
|
public DeviceProfile findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey) {
|
||||||
log.trace("Executing findDeviceProfileIdByCredentialsId credentialId [{}]", certificateHash);
|
log.trace("Executing findDeviceProfileIdByCredentialsId credentialId [{}]", provisionDeviceKey);
|
||||||
validateString(certificateHash, INCORRECT_DEVICE_PROFILE_CREDENTIALS_HASH + certificateHash);
|
validateString(provisionDeviceKey, INCORRECT_PROVISION_DEVICE_KEY + provisionDeviceKey);
|
||||||
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromCertificateHash(certificateHash),
|
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey),
|
||||||
() -> deviceProfileDao.findByCertificateHash(certificateHash), true);
|
() -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -290,7 +290,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
@Override
|
@Override
|
||||||
public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing setDefaultDeviceProfile [{}]", 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());
|
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
||||||
if (!deviceProfile.isDefault()) {
|
if (!deviceProfile.isDefault()) {
|
||||||
deviceProfile.setDefault(true);
|
deviceProfile.setDefault(true);
|
||||||
@ -350,14 +350,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void formatDeviceProfileCertificate(DeviceProfile deviceProfile, X509CertificateChainProvisionConfiguration x509Configuration) {
|
private void formatDeviceProfileCertificate(DeviceProfile deviceProfile, X509CertificateChainProvisionConfiguration x509Configuration) {
|
||||||
String formattedCertificateValue = formatCertificateValue(x509Configuration.getCertificateValue());
|
String formattedCertificateValue = formatCertificateValue(x509Configuration.getProvisionDeviceSecret());
|
||||||
String cert = fetchLeafCertificateFromChain(formattedCertificateValue);
|
String cert = fetchLeafCertificateFromChain(formattedCertificateValue);
|
||||||
String sha3Hash = EncryptionUtil.getSha3Hash(cert);
|
String sha3Hash = EncryptionUtil.getSha3Hash(cert);
|
||||||
DeviceProfileData deviceProfileData = deviceProfile.getProfileData();
|
DeviceProfileData deviceProfileData = deviceProfile.getProfileData();
|
||||||
x509Configuration.setCertificateValue(formattedCertificateValue);
|
x509Configuration.setProvisionDeviceSecret(formattedCertificateValue);
|
||||||
deviceProfileData.setProvisionConfiguration(x509Configuration);
|
deviceProfileData.setProvisionConfiguration(x509Configuration);
|
||||||
deviceProfile.setProfileData(deviceProfileData);
|
deviceProfile.setProfileData(deviceProfileData);
|
||||||
deviceProfile.setCertificateHash(sha3Hash);
|
deviceProfile.setProvisionDeviceKey(sha3Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String fetchLeafCertificateFromChain(String value) {
|
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_PROVISION_DEVICE_KEY = "provision_device_key";
|
||||||
public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
|
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_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";
|
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)
|
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
|
||||||
private UUID externalId;
|
private UUID externalId;
|
||||||
|
|
||||||
@Column(name = ModelConstants.DEVICE_PROFILE_CERTIFICATE_HASH_PROPERTY)
|
|
||||||
private String certificateHash;
|
|
||||||
|
|
||||||
public DeviceProfileEntity() {
|
public DeviceProfileEntity() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -129,7 +126,6 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
|
|||||||
this.image = deviceProfile.getImage();
|
this.image = deviceProfile.getImage();
|
||||||
this.transportType = deviceProfile.getTransportType();
|
this.transportType = deviceProfile.getTransportType();
|
||||||
this.provisionType = deviceProfile.getProvisionType();
|
this.provisionType = deviceProfile.getProvisionType();
|
||||||
this.certificateHash = deviceProfile.getCertificateHash();
|
|
||||||
this.description = deviceProfile.getDescription();
|
this.description = deviceProfile.getDescription();
|
||||||
this.isDefault = deviceProfile.isDefault();
|
this.isDefault = deviceProfile.isDefault();
|
||||||
this.profileData = JacksonUtil.convertValue(deviceProfile.getProfileData(), ObjectNode.class);
|
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.setDefaultDashboardId(new DashboardId(defaultDashboardId));
|
||||||
}
|
}
|
||||||
deviceProfile.setProvisionDeviceKey(provisionDeviceKey);
|
deviceProfile.setProvisionDeviceKey(provisionDeviceKey);
|
||||||
deviceProfile.setCertificateHash(certificateHash);
|
|
||||||
|
|
||||||
if (firmwareId != null) {
|
if (firmwareId != null) {
|
||||||
deviceProfile.setFirmwareId(new OtaPackageId(firmwareId));
|
deviceProfile.setFirmwareId(new OtaPackageId(firmwareId));
|
||||||
|
|||||||
@ -132,14 +132,14 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
|||||||
if (deviceProfile.getProvisionType() == null) {
|
if (deviceProfile.getProvisionType() == null) {
|
||||||
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
|
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
|
||||||
}
|
}
|
||||||
if (deviceProfile.getCertificateHash() != null) {
|
if (deviceProfile.getProvisionDeviceKey() != null) {
|
||||||
if (getRootCAFromJavaCacerts(deviceProfile.getCertificateHash())) {
|
if (getRootCAFromJavaCacerts(deviceProfile.getProvisionDeviceKey())) {
|
||||||
throw new DataValidationException("Device profile certificate cannot be well known root CA!");
|
throw new DataValidationException("Device profile certificate cannot be well known root CA!");
|
||||||
}
|
}
|
||||||
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
// DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByProvisionDeviceKey(deviceProfile.getProvisionDeviceKey());
|
||||||
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(deviceProfile.getId())) {
|
// if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(deviceProfile.getId())) {
|
||||||
throw new DataValidationException("Device profile with such certificate hash already exists!");
|
// throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
|
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
|
||||||
transportConfiguration.validate();
|
transportConfiguration.validate();
|
||||||
@ -234,14 +234,14 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
|||||||
throw new DataValidationException(message);
|
throw new DataValidationException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deviceProfile.getCertificateHash() != null) {
|
if (deviceProfile.getProvisionDeviceKey() != null) {
|
||||||
if (getRootCAFromJavaCacerts(deviceProfile.getCertificateHash())) {
|
if (getRootCAFromJavaCacerts(deviceProfile.getProvisionDeviceKey())) {
|
||||||
throw new DataValidationException("Device profile certificate cannot be well known root CA!");
|
throw new DataValidationException("Device profile certificate cannot be well known root CA!");
|
||||||
}
|
}
|
||||||
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
// DeviceProfile existingDeviceProfileWithDeviceKey = deviceProfileDao.findByProvisionDeviceKey(deviceProfile.getProvisionDeviceKey());
|
||||||
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(old.getId())) {
|
// if (existingDeviceProfileWithDeviceKey != null && !existingDeviceProfileWithDeviceKey.getId().equals(old.getId())) {
|
||||||
throw new DataValidationException("Device profile with such certificate hash already exists!");
|
// throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,8 +64,6 @@ 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);
|
||||||
|
|||||||
@ -115,11 +115,6 @@ 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));
|
||||||
|
|||||||
@ -295,10 +295,8 @@ CREATE TABLE IF NOT EXISTS device_profile (
|
|||||||
default_dashboard_id uuid,
|
default_dashboard_id uuid,
|
||||||
default_queue_name varchar(255),
|
default_queue_name varchar(255),
|
||||||
provision_device_key varchar,
|
provision_device_key varchar,
|
||||||
certificate_hash varchar,
|
|
||||||
default_edge_rule_chain_id uuid,
|
default_edge_rule_chain_id uuid,
|
||||||
external_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_profile_name_unq_key UNIQUE (tenant_id, name),
|
||||||
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
|
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
|
||||||
CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
|
CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user