Browse Source

Review and improvements

pull/7935/head
Andrii Shvaika 3 years ago
parent
commit
5d84945ccd
  1. 84
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  2. 38
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  3. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java
  4. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/provision/ProvisionFailedException.java
  5. 14
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java

84
application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java

@ -99,9 +99,44 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
this.partitionService = partitionService;
}
@Override
public ProvisionResponse provisionDeviceViaX509Chain(DeviceProfile targetProfile, ProvisionRequest provisionRequest) {
if (targetProfile == null) {
throw new ProvisionFailedException("Device profile is not specified!");
}
if (!DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN.equals(targetProfile.getProfileData().getProvisionConfiguration().getType())) {
throw new ProvisionFailedException("Device profile provision strategy is not X509_CERTIFICATE_CHAIN!");
}
X509CertificateChainProvisionConfiguration configuration = (X509CertificateChainProvisionConfiguration) targetProfile.getProfileData().getProvisionConfiguration();
String certificateValue = provisionRequest.getCredentialsData().getX509CertHash();
String certificateRegEx = configuration.getCertificateRegExPattern();
String deviceName = extractDeviceNameFromCertificateCNByRegEx(targetProfile, certificateValue, certificateRegEx);
if (StringUtils.isBlank(deviceName)) {
log.warn("Device name cannot be extracted using regex [{}] for certificate [{}]", certificateRegEx, certificateValue);
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
}
provisionRequest.setDeviceName(deviceName);
Device targetDevice = deviceService.findDeviceByTenantIdAndName(targetProfile.getTenantId(), provisionRequest.getDeviceName());
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.warn("[{}][{}] Device with name {} doesn't exist and cannot be created due incorrect configuration for X509CertificateChainProvisionConfiguration",
targetProfile.getTenantId(), targetProfile.getId(), provisionRequest.getDeviceName());
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
}
}
@Override
public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) {
fetchAndApplyDeviceNameForX509ProvisionRequestWithRegEx(provisionRequest);
String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey();
String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret();
if (!StringUtils.isEmpty(provisionRequest.getDeviceName())) {
@ -148,24 +183,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
}
break;
case X509_CERTIFICATE_CHAIN:
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) {
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.warn("Device with name {} doesn't exist and cannot be created due incorrect configuration for X509CertificateChainProvisionConfiguration", provisionRequest.getDeviceName());
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
}
}
break;
throw new ProvisionFailedException("Invalid provision strategy type!");
}
throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
}
@ -277,31 +295,21 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
auditLogService.logEntityAction(tenantId, customerId, new UserId(UserId.NULL_UUID), device.getName(), device.getId(), device, actionType, null, provisionRequest);
}
private void fetchAndApplyDeviceNameForX509ProvisionRequestWithRegEx(ProvisionRequest provisionRequest) {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(provisionRequest.getCredentials().getProvisionDeviceKey());
if (deviceProfile != null && DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN.equals(deviceProfile.getProfileData().getProvisionConfiguration().getType())) {
X509CertificateChainProvisionConfiguration configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration();
String certificateValue = provisionRequest.getCredentialsData().getX509CertHash();
String certificateRegEx = configuration.getCertificateRegExPattern();
String deviceName = extractDeviceNameFromCertificateCNByRegEx(certificateValue, certificateRegEx);
if (deviceName == null) {
log.warn("Device name cannot be extracted using regex [{}] for certificate [{}]",certificateRegEx, certificateValue);
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
}
provisionRequest.setDeviceName(deviceName);
}
}
private String extractDeviceNameFromCertificateCNByRegEx(String x509Value, String regex) {
private String extractDeviceNameFromCertificateCNByRegEx(DeviceProfile profile, String x509Value, String regex) {
try {
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);
if (matcher.find()) {
return matcher.group(0);
return matcher.group(1);
} else {
return null;
}
} catch (Exception ignored) {}
return null;
} catch (Exception ignored) {
log.trace("[{}][{}] Failed to extract device name using [{}] and certificate: [{}]", profile.getTenantId(), profile.getId(), regex, x509Value);
return null;
}
}
}

38
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -242,17 +242,20 @@ public class DefaultTransportApiService implements TransportApiService {
for (String certificateValue : chain) {
String certificateHash = EncryptionUtil.getSha3Hash(certificateValue);
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(certificateHash);
if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
if (credentials != null && DeviceCredentialsType.X509_CERTIFICATE.equals(credentials.getCredentialsType())) {
return getDeviceInfo(credentials);
}
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(certificateHash);
if (deviceProfile != null && deviceProfile.getProvisionType() == X509_CERTIFICATE_CHAIN) {
if (deviceProfile != null && X509_CERTIFICATE_CHAIN.equals(deviceProfile.getProvisionType())) {
String updatedDeviceProvisionSecret = chain.get(0);
ProvisionRequest provisionRequest = createProvisionRequest(deviceProfile, updatedDeviceProvisionSecret);
ProvisionResponse provisionResponse = provisionDeviceRequestAndGetResponse(provisionRequest);
if (provisionResponse != null && ProvisionResponseStatus.SUCCESS.equals(provisionResponse.getResponseStatus())) {
return getDeviceInfo(provisionResponse.getDeviceCredentials());
} else {
ProvisionRequest provisionRequest = createProvisionRequest(updatedDeviceProvisionSecret);
try {
ProvisionResponse provisionResponse = deviceProvisionService.provisionDeviceViaX509Chain(deviceProfile, provisionRequest);
if (ProvisionResponseStatus.SUCCESS.equals(provisionResponse.getResponseStatus())) {
return getDeviceInfo(provisionResponse.getDeviceCredentials());
}
} catch (ProvisionFailedException e) {
log.debug("[{}][{}] Failed to provision device with cert chain: {}", deviceProfile.getTenantId(), deviceProfile.getId(), provisionRequest, e);
return getEmptyTransportApiResponseFuture();
}
} else if (deviceProfile != null) {
@ -702,23 +705,10 @@ public class DefaultTransportApiService implements TransportApiService {
return l != null ? l : 0;
}
private ProvisionRequest createProvisionRequest(DeviceProfile deviceProfile, String certificateValue) {
ProvisionDeviceProfileCredentials provisionDeviceProfileCredentials = new ProvisionDeviceProfileCredentials(
deviceProfile.getProvisionDeviceKey(),
deviceProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret()
);
ProvisionDeviceCredentialsData provisionDeviceCredentialsData = new ProvisionDeviceCredentialsData(null, null, null, null, certificateValue);
return new ProvisionRequest(null, DeviceCredentialsType.X509_CERTIFICATE, provisionDeviceCredentialsData, provisionDeviceProfileCredentials);
}
private ProvisionResponse provisionDeviceRequestAndGetResponse(ProvisionRequest provisionRequest) {
try {
return deviceProvisionService.provisionDevice(provisionRequest);
} catch (ProvisionFailedException e) {
log.error(e.getMessage());
}
return null;
private ProvisionRequest createProvisionRequest(String certificateValue) {
return new ProvisionRequest(null, DeviceCredentialsType.X509_CERTIFICATE,
new ProvisionDeviceCredentialsData(null, null, null, null, certificateValue),
null);
}
}

3
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
@ -22,4 +23,6 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponse;
public interface DeviceProvisionService {
ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) throws ProvisionFailedException;
ProvisionResponse provisionDeviceViaX509Chain(DeviceProfile deviceProfile, ProvisionRequest provisionRequest);
}

4
common/dao-api/src/main/java/org/thingsboard/server/dao/device/provision/ProvisionFailedException.java

@ -16,7 +16,11 @@
package org.thingsboard.server.dao.device.provision;
public class ProvisionFailedException extends RuntimeException {
private static final long serialVersionUID = 1673991117668477401L;
public ProvisionFailedException(String errorMsg) {
super(errorMsg);
}
}

14
dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service.validator;
import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -72,6 +73,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Slf4j
@Component
public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<DeviceProfile> {
@ -98,10 +100,10 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
@Autowired
private DashboardService dashboardService;
@Value("${security.java_cacerts.path}")
@Value("${security.java_cacerts.path:}")
private String javaCacertsPath;
@Value("${security.java_cacerts.password}")
@Value("${security.java_cacerts.password:}")
private String javaCacertsPassword;
@Override
@ -343,13 +345,13 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
if (!uris.add(uri)) {
throw new DeviceCredentialsValidationException(server + " \"Host + port\" value = " + uri + ". This value must be a unique value for all servers!");
}
Integer port;
int port;
if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) {
port = serverConfig.isBootstrapServerIs() ? 5687 : 5685;
} else {
port = serverConfig.isBootstrapServerIs() ? 5688 : 5686;
}
if (serverConfig.getPort() == null || serverConfig.getPort().intValue() != port) {
if (serverConfig.getPort() == null || serverConfig.getPort() != port) {
throw new DeviceCredentialsValidationException(server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!");
}
}
@ -406,8 +408,8 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
return true;
}
}
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException | IOException ignored) {
} catch (Exception ignored) {
log.trace("Failed to validate certificate due to: ", ignored);
}
return false;
}

Loading…
Cancel
Save