Fix CacheKey toString and validation logs
This commit is contained in:
parent
ba0ae08719
commit
e8543f39d5
@ -258,8 +258,7 @@ public class DefaultTransportApiService implements TransportApiService {
|
|||||||
}
|
}
|
||||||
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(certificateHash);
|
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(certificateHash);
|
||||||
if (deviceProfile != null) {
|
if (deviceProfile != null) {
|
||||||
String deviceCN = extractDeviceNameFromCNByRegEx(SslUtil.parseCommonName(chain.get(0)), deviceProfile.getCertificateRegexPattern());
|
String deviceName = extractDeviceNameFromCNByRegEx(SslUtil.parseCommonName(chain.get(0)), deviceProfile.getCertificateRegexPattern());
|
||||||
String deviceName = extractDeviceNameFromCNByRegEx(deviceCN, deviceProfile.getCertificateRegexPattern());
|
|
||||||
Device device = deviceService.findDeviceByTenantIdAndName(deviceProfile.getTenantId(), deviceName);
|
Device device = deviceService.findDeviceByTenantIdAndName(deviceProfile.getTenantId(), deviceName);
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
|
||||||
@ -275,7 +274,6 @@ public class DefaultTransportApiService implements TransportApiService {
|
|||||||
deviceCredentials = updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
|
deviceCredentials = updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
|
||||||
return getDeviceInfo(deviceCredentials);
|
return getDeviceInfo(deviceCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (CertificateEncodingException e) {
|
} catch (CertificateEncodingException e) {
|
||||||
|
|||||||
@ -422,6 +422,7 @@ message CredentialsDataProto {
|
|||||||
ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1;
|
ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1;
|
||||||
ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2;
|
ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2;
|
||||||
ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3;
|
ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3;
|
||||||
|
ValidateOrCreateDeviceX509CertRequestMsg validateOrCreateDeviceX509CertRequestMsg = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProvisionDeviceRequestMsg {
|
message ProvisionDeviceRequestMsg {
|
||||||
|
|||||||
@ -66,13 +66,13 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
|
|||||||
private DeviceTransportType transportType;
|
private DeviceTransportType transportType;
|
||||||
@ApiModelProperty(position = 15, value = "Provisioning strategy.")
|
@ApiModelProperty(position = 15, value = "Provisioning strategy.")
|
||||||
private DeviceProfileProvisionType provisionType;
|
private DeviceProfileProvisionType provisionType;
|
||||||
@ApiModelProperty(position = 16, value = "CA certificate value. ")
|
@ApiModelProperty(position = 18, value = "CA certificate value. ")
|
||||||
private String certificateValue;
|
private String certificateValue;
|
||||||
@ApiModelProperty(position = 17, value = "CA certificate hash. ")
|
@ApiModelProperty(position = 19, value = "CA certificate hash. ")
|
||||||
private String certificateHash;
|
private String certificateHash;
|
||||||
@ApiModelProperty(position = 18, value = "Regex to fetch deviceName from CN. ")
|
@ApiModelProperty(position = 20, value = "Regex to fetch deviceName from CN. ")
|
||||||
private String certificateRegexPattern;
|
private String certificateRegexPattern;
|
||||||
@ApiModelProperty(position = 19, value = "Allow to create new devices by x509 provision strategy. ")
|
@ApiModelProperty(position = 21, value = "Allow to create new devices by x509 provision strategy. ")
|
||||||
private boolean allowCreateNewDevicesByX509Strategy;
|
private boolean allowCreateNewDevicesByX509Strategy;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,6 @@ package org.thingsboard.server.transport.mqtt;
|
|||||||
|
|
||||||
import io.netty.handler.ssl.SslHandler;
|
import io.netty.handler.ssl.SslHandler;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.checkerframework.checker.units.qual.C;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -161,14 +160,13 @@ public class MqttSslHandlerProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable e) {
|
public void onError(Throwable e) {
|
||||||
// to fix this error, cuz no one can understand this ...
|
log.trace("Failed to process certificate chain: {}", certificateChain, e);
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
latch.await(10, TimeUnit.SECONDS);
|
latch.await(10, TimeUnit.SECONDS);
|
||||||
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
|
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
|
||||||
throw new CertificateException("Invalid Certificate's chain");
|
throw new CertificateException("Invalid Certificate's chain. Cannot find such device credentials.");
|
||||||
}
|
}
|
||||||
} catch (CertificateEncodingException | InterruptedException e) {
|
} catch (CertificateEncodingException | InterruptedException e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
|
|||||||
@ -877,7 +877,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
|
|||||||
private X509Certificate getX509Certificate() {
|
private X509Certificate getX509Certificate() {
|
||||||
try {
|
try {
|
||||||
Certificate[] certChain = sslHandler.engine().getSession().getPeerCertificates();
|
Certificate[] certChain = sslHandler.engine().getSession().getPeerCertificates();
|
||||||
if (certChain.length > 1) {
|
if (certChain.length > 0) {
|
||||||
return (X509Certificate) certChain[0];
|
return (X509Certificate) certChain[0];
|
||||||
}
|
}
|
||||||
} catch (SSLPeerUnverifiedException e) {
|
} catch (SSLPeerUnverifiedException e) {
|
||||||
|
|||||||
@ -56,14 +56,18 @@ public class DeviceProfileCacheKey implements Serializable {
|
|||||||
return new DeviceProfileCacheKey(tenantId, null, null, null, true);
|
return new DeviceProfileCacheKey(tenantId, null, null, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMPORTANT: Method toString() has to return unique value, if you add additional field to this class, please also refactor toString().
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (deviceProfileId != null) {
|
if (deviceProfileId != null) {
|
||||||
return deviceProfileId.toString();
|
return deviceProfileId.toString();
|
||||||
} else if (defaultProfile) {
|
} else if (defaultProfile) {
|
||||||
return tenantId.toString();
|
return tenantId.toString();
|
||||||
} else {
|
} else if (certificateHash != null) {
|
||||||
return tenantId + "_" + name;
|
return certificateHash;
|
||||||
}
|
}
|
||||||
|
return tenantId + "_" + name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,5 +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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,6 +98,9 @@ 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) {
|
||||||
|
keys.add(DeviceProfileCacheKey.fromCertificateHash(event.getCertificateHash()));
|
||||||
|
}
|
||||||
cache.evict(keys);
|
cache.evict(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +138,12 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
try {
|
try {
|
||||||
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));
|
||||||
} 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));
|
||||||
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!",
|
||||||
@ -178,7 +183,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
deleteEntityRelations(tenantId, deviceProfileId);
|
deleteEntityRelations(tenantId, deviceProfileId);
|
||||||
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));
|
||||||
} 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")) {
|
||||||
@ -284,14 +290,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (previousDefaultDeviceProfile == null) {
|
if (previousDefaultDeviceProfile == null) {
|
||||||
deviceProfileDao.save(tenantId, deviceProfile);
|
deviceProfileDao.save(tenantId, deviceProfile);
|
||||||
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(), null, deviceProfile.getId(), true));
|
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(), null, deviceProfile.getId(), true, null));
|
||||||
changed = true;
|
changed = true;
|
||||||
} else if (!previousDefaultDeviceProfile.getId().equals(deviceProfile.getId())) {
|
} else if (!previousDefaultDeviceProfile.getId().equals(deviceProfile.getId())) {
|
||||||
previousDefaultDeviceProfile.setDefault(false);
|
previousDefaultDeviceProfile.setDefault(false);
|
||||||
deviceProfileDao.save(tenantId, previousDefaultDeviceProfile);
|
deviceProfileDao.save(tenantId, previousDefaultDeviceProfile);
|
||||||
deviceProfileDao.save(tenantId, deviceProfile);
|
deviceProfileDao.save(tenantId, deviceProfile);
|
||||||
publishEvictEvent(new DeviceProfileEvictEvent(previousDefaultDeviceProfile.getTenantId(), previousDefaultDeviceProfile.getName(), null, previousDefaultDeviceProfile.getId(), false));
|
publishEvictEvent(new DeviceProfileEvictEvent(previousDefaultDeviceProfile.getTenantId(), previousDefaultDeviceProfile.getName(), null, previousDefaultDeviceProfile.getId(), false, null));
|
||||||
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(), null, deviceProfile.getId(), true));
|
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(), null, deviceProfile.getId(), true, null));
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
@ -336,13 +342,17 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void formatDeviceProfileCertificate(DeviceProfile deviceProfile) {
|
private void formatDeviceProfileCertificate(DeviceProfile deviceProfile) {
|
||||||
String cert = regexCertificateChain(deviceProfile.getCertificateValue());
|
String certificateValue = deviceProfile.getCertificateValue();
|
||||||
|
String cert = regexCertificateChain(certificateValue);
|
||||||
String sha3Hash = EncryptionUtil.getSha3Hash(cert);
|
String sha3Hash = EncryptionUtil.getSha3Hash(cert);
|
||||||
deviceProfile.setCertificateHash(sha3Hash);
|
deviceProfile.setCertificateHash(sha3Hash);
|
||||||
|
if (!isCertificateChain(certificateValue)) {
|
||||||
|
deviceProfile.setCertificateValue(EncryptionUtil.certTrimNewLines(certificateValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String regexCertificateChain(String chain) {
|
private String regexCertificateChain(String chain) {
|
||||||
String regex = "-----BEGIN CERTIFICATE-----\\s*((.+\\s+)*?)-----END CERTIFICATE----";
|
String regex = "-----BEGIN CERTIFICATE-----\\s*((.+\\s+)*?)-----END CERTIFICATE-----";
|
||||||
Pattern pattern = Pattern.compile(regex);
|
Pattern pattern = Pattern.compile(regex);
|
||||||
Matcher matcher = pattern.matcher(chain);
|
Matcher matcher = pattern.matcher(chain);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
@ -351,4 +361,9 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
|
|||||||
return chain;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCertificateChain(String certificateValue) {
|
||||||
|
int count = certificateValue.split("-----BEGIN CERTIFICATE", -1).length - 1;
|
||||||
|
return count > 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,7 +138,7 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
|||||||
}
|
}
|
||||||
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
||||||
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(deviceProfile.getId())) {
|
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(deviceProfile.getId())) {
|
||||||
throw new DataValidationException("Cannot create device profile with certificate because such certificate already exists!");
|
throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
|
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
|
||||||
@ -240,7 +240,7 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
|||||||
}
|
}
|
||||||
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
DeviceProfile existingDeviceProfileCertificate = deviceProfileDao.findByCertificateHash(deviceProfile.getCertificateHash());
|
||||||
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(old.getId())) {
|
if (existingDeviceProfileCertificate != null && !existingDeviceProfileCertificate.getId().equals(old.getId())) {
|
||||||
throw new DataValidationException("Can't change device profile certificate because such certificate already exists!");
|
throw new DataValidationException("Device profile with such certificate hash already exists!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return old;
|
return old;
|
||||||
@ -396,8 +396,7 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean getRootCAFromJavaCacerts(String deviceProfileHash) {
|
boolean getRootCAFromJavaCacerts(String deviceProfileHash) {
|
||||||
Set<String> rootCa = new HashSet<>();
|
|
||||||
try {
|
try {
|
||||||
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
|
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
|
||||||
FileInputStream is = new FileInputStream(filename);
|
FileInputStream is = new FileInputStream(filename);
|
||||||
@ -408,12 +407,14 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
|
|||||||
PKIXParameters params = new PKIXParameters(keystore);
|
PKIXParameters params = new PKIXParameters(keystore);
|
||||||
for (TrustAnchor ta : params.getTrustAnchors()) {
|
for (TrustAnchor ta : params.getTrustAnchors()) {
|
||||||
X509Certificate cert = ta.getTrustedCert();
|
X509Certificate cert = ta.getTrustedCert();
|
||||||
rootCa.add(EncryptionUtil.getSha3Hash(getCertificateString(cert)));
|
if (EncryptionUtil.getSha3Hash(getCertificateString(cert)).equals(deviceProfileHash)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException |
|
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException |
|
||||||
InvalidAlgorithmParameterException | IOException ignored) {
|
InvalidAlgorithmParameterException | IOException ignored) {
|
||||||
}
|
}
|
||||||
return rootCa.contains(deviceProfileHash);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCertificateString(Certificate cert) throws CertificateEncodingException {
|
private String getCertificateString(Certificate cert) throws CertificateEncodingException {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user