Browse Source

Review and improvements

pull/7935/head
Andrii Shvaika 4 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; 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 @Override
public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) { public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) {
fetchAndApplyDeviceNameForX509ProvisionRequestWithRegEx(provisionRequest);
String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey(); String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey();
String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret();
if (!StringUtils.isEmpty(provisionRequest.getDeviceName())) { if (!StringUtils.isEmpty(provisionRequest.getDeviceName())) {
@ -148,24 +183,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
} }
break; break;
case X509_CERTIFICATE_CHAIN: case X509_CERTIFICATE_CHAIN:
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { throw new ProvisionFailedException("Invalid provision strategy type!");
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(ProvisionResponseStatus.NOT_FOUND.name()); 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); auditLogService.logEntityAction(tenantId, customerId, new UserId(UserId.NULL_UUID), device.getName(), device.getId(), device, actionType, null, provisionRequest);
} }
private void fetchAndApplyDeviceNameForX509ProvisionRequestWithRegEx(ProvisionRequest provisionRequest) { private String extractDeviceNameFromCertificateCNByRegEx(DeviceProfile profile, String x509Value, String regex) {
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) {
try { try {
String commonName = SslUtil.parseCommonName(SslUtil.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);
if (matcher.find()) { if (matcher.find()) {
return matcher.group(0); return matcher.group(1);
} else {
return null;
} }
} catch (Exception ignored) {} } catch (Exception ignored) {
return null; 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) { for (String certificateValue : chain) {
String certificateHash = EncryptionUtil.getSha3Hash(certificateValue); String certificateHash = EncryptionUtil.getSha3Hash(certificateValue);
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(certificateHash); 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); return getDeviceInfo(credentials);
} }
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(certificateHash); 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); String updatedDeviceProvisionSecret = chain.get(0);
ProvisionRequest provisionRequest = createProvisionRequest(deviceProfile, updatedDeviceProvisionSecret); ProvisionRequest provisionRequest = createProvisionRequest(updatedDeviceProvisionSecret);
ProvisionResponse provisionResponse = provisionDeviceRequestAndGetResponse(provisionRequest); try {
if (provisionResponse != null && ProvisionResponseStatus.SUCCESS.equals(provisionResponse.getResponseStatus())) { ProvisionResponse provisionResponse = deviceProvisionService.provisionDeviceViaX509Chain(deviceProfile, provisionRequest);
return getDeviceInfo(provisionResponse.getDeviceCredentials()); if (ProvisionResponseStatus.SUCCESS.equals(provisionResponse.getResponseStatus())) {
} else { return getDeviceInfo(provisionResponse.getDeviceCredentials());
}
} catch (ProvisionFailedException e) {
log.debug("[{}][{}] Failed to provision device with cert chain: {}", deviceProfile.getTenantId(), deviceProfile.getId(), provisionRequest, e);
return getEmptyTransportApiResponseFuture(); return getEmptyTransportApiResponseFuture();
} }
} else if (deviceProfile != null) { } else if (deviceProfile != null) {
@ -702,23 +705,10 @@ public class DefaultTransportApiService implements TransportApiService {
return l != null ? l : 0; return l != null ? l : 0;
} }
private ProvisionRequest createProvisionRequest(DeviceProfile deviceProfile, String certificateValue) { private ProvisionRequest createProvisionRequest(String certificateValue) {
ProvisionDeviceProfileCredentials provisionDeviceProfileCredentials = new ProvisionDeviceProfileCredentials( return new ProvisionRequest(null, DeviceCredentialsType.X509_CERTIFICATE,
deviceProfile.getProvisionDeviceKey(), new ProvisionDeviceCredentialsData(null, null, null, null, certificateValue),
deviceProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret() null);
);
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;
} }
} }

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

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.device; 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.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;
@ -22,4 +23,6 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponse;
public interface DeviceProvisionService { public interface DeviceProvisionService {
ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) throws ProvisionFailedException; 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; package org.thingsboard.server.dao.device.provision;
public class ProvisionFailedException extends RuntimeException { public class ProvisionFailedException extends RuntimeException {
private static final long serialVersionUID = 1673991117668477401L;
public ProvisionFailedException(String errorMsg) { public ProvisionFailedException(String errorMsg) {
super(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.Descriptors;
import com.google.protobuf.DynamicMessage; import com.google.protobuf.DynamicMessage;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.SecurityUtil; import org.eclipse.leshan.core.util.SecurityUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -72,6 +73,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@Slf4j
@Component @Component
public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<DeviceProfile> { public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<DeviceProfile> {
@ -98,10 +100,10 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
@Autowired @Autowired
private DashboardService dashboardService; private DashboardService dashboardService;
@Value("${security.java_cacerts.path}") @Value("${security.java_cacerts.path:}")
private String javaCacertsPath; private String javaCacertsPath;
@Value("${security.java_cacerts.password}") @Value("${security.java_cacerts.password:}")
private String javaCacertsPassword; private String javaCacertsPassword;
@Override @Override
@ -343,13 +345,13 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
if (!uris.add(uri)) { if (!uris.add(uri)) {
throw new DeviceCredentialsValidationException(server + " \"Host + port\" value = " + uri + ". This value must be a unique value for all servers!"); 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())) { if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) {
port = serverConfig.isBootstrapServerIs() ? 5687 : 5685; port = serverConfig.isBootstrapServerIs() ? 5687 : 5685;
} else { } else {
port = serverConfig.isBootstrapServerIs() ? 5688 : 5686; 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 + "!"); 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; return true;
} }
} }
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | } catch (Exception ignored) {
InvalidAlgorithmParameterException | IOException ignored) { log.trace("Failed to validate certificate due to: ", ignored);
} }
return false; return false;
} }

Loading…
Cancel
Save