From 1d9132b0c65d8860219e310327e2b80e40a5532e Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 10 Nov 2021 08:38:04 +0200 Subject: [PATCH 1/6] lwm2m - change bootstrapConfig --- .../secure/LwM2MBootstrapConfig.java | 39 ++++++++++--------- .../secure/LwM2MBootstrapSecurityStore.java | 23 +++++------ ...LwM2mCredentialsSecurityInfoValidator.java | 23 ++++++----- .../TbLwM2MDtlsCertificateVerifier.java | 4 +- .../lwm2m/secure/TbLwM2MSecurityInfo.java | 2 +- ...tials.java => LwM2MClientCredentials.java} | 2 +- 6 files changed, 47 insertions(+), 46 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/{LwM2MCredentials.java => LwM2MClientCredentials.java} (96%) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java index 29024d8f8f..2340267926 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure; import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.Setter; @@ -31,10 +32,10 @@ import java.nio.charset.StandardCharsets; import java.util.List; @Data +@AllArgsConstructor public class LwM2MBootstrapConfig implements Serializable { - List serversConfiguration; - LwM2MBootstrapClientCredential bootstrapClientCredential; + List serverConfiguration; /* interface BootstrapSecurityConfig servers: BootstrapServersSecurityConfig, @@ -49,9 +50,9 @@ public class LwM2MBootstrapConfig implements Serializable { * notifIfDisabled: boolean, * binding: string * */ - @Getter - @Setter - private LwM2MBootstrapServers servers; +// @Getter +// @Setter +// private LwM2MBootstrapServers servers; /** -bootstrapServer, lwm2mServer * interface ServerSecurityConfig @@ -68,15 +69,15 @@ public class LwM2MBootstrapConfig implements Serializable { * */ @Getter @Setter - private LwM2MServerBootstrap bootstrapServer; + private LwM2MBootstrapClientCredential bootstrapServer; @Getter @Setter - private LwM2MServerBootstrap lwm2mServer; + private LwM2MBootstrapClientCredential lwm2mServer; + + public LwM2MBootstrapConfig(List serverConfiguration, LwM2MBootstrapClientCredential bootstrapClientCredential) { + this.serverConfiguration = serverConfiguration; - public LwM2MBootstrapConfig(List serversConfiguration, LwM2MBootstrapClientCredential bootstrapClientCredential) { - this.serversConfiguration = serversConfiguration; - this.bootstrapClientCredential = bootstrapClientCredential; } @JsonIgnore @@ -87,17 +88,17 @@ public class LwM2MBootstrapConfig implements Serializable { configBs.toDelete.add("/1"); /* Server Configuration (object 1) as defined in LWM2M 1.0.x TS. */ BootstrapConfig.ServerConfig server0 = new BootstrapConfig.ServerConfig(); - server0.shortId = servers.getShortId(); - server0.lifetime = servers.getLifetime(); - server0.defaultMinPeriod = servers.getDefaultMinPeriod(); - server0.notifIfDisabled = servers.isNotifIfDisabled(); - server0.binding = BindingMode.parse(servers.getBinding()); +// server0.shortId = servers.getShortId(); +// server0.lifetime = servers.getLifetime(); +// server0.defaultMinPeriod = servers.getDefaultMinPeriod(); +// server0.notifIfDisabled = servers.isNotifIfDisabled(); +// server0.binding = BindingMode.parse(servers.getBinding()); configBs.servers.put(0, server0); /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */ - this.bootstrapServer.setBootstrapServerIs(true); - configBs.security.put(0, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.bootstrapServer.isBootstrapServerIs(), this.bootstrapServer.getSecurityMode(), this.bootstrapServer.getClientPublicKeyOrId(), this.bootstrapServer.getServerPublicKey(), this.bootstrapServer.getClientSecretKey(), this.bootstrapServer.getServerId())); - /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Server instance = 1 */ - configBs.security.put(1, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.lwm2mServer.isBootstrapServerIs(), this.lwm2mServer.getSecurityMode(), this.lwm2mServer.getClientPublicKeyOrId(), this.lwm2mServer.getServerPublicKey(), this.lwm2mServer.getClientSecretKey(), this.lwm2mServer.getServerId())); +// this.bootstrapServer.setBootstrapServerIs(true); +// configBs.security.put(0, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.bootstrapServer.isBootstrapServerIs(), this.bootstrapServer.getSecurityMode(), this.bootstrapServer.getClientPublicKeyOrId(), this.bootstrapServer.getServerPublicKey(), this.bootstrapServer.getClientSecretKey(), this.bootstrapServer.getServerId())); +// /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Server instance = 1 */ +// configBs.security.put(1, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.lwm2mServer.isBootstrapServerIs(), this.lwm2mServer.getSecurityMode(), this.lwm2mServer.getClientPublicKeyOrId(), this.lwm2mServer.getServerPublicKey(), this.lwm2mServer.getClientSecretKey(), this.lwm2mServer.getServerId())); return configBs; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 935e95c546..c2c32ca79c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -71,6 +71,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public Iterator getAllByEndpoint(String endPoint) { + // TODO TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP); if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { /* add value to store from BootstrapJson */ @@ -125,20 +126,20 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { switch (lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode()) { /* Use RPK only */ case PSK: - store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndpoint(), - lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId(), - Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientSecretKey().toCharArray()))); +// store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndpoint(), +// lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId(), +// Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientSecretKey().toCharArray()))); store.setSecurityMode(SecurityMode.PSK); break; case RPK: - try { - store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndpoint(), - SecurityUtil.publicKey.decode(Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId().toCharArray())))); - store.setSecurityMode(SecurityMode.RPK); - break; - } catch (IOException | GeneralSecurityException e) { - log.error("Unable to decode Client public key for [{}] [{}]", store.getEndpoint(), e.getMessage()); - } +// try { +//// store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndpoint(), +//// SecurityUtil.publicKey.decode(Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId().toCharArray())))); +//// store.setSecurityMode(SecurityMode.RPK); +// break; +// } catch (IOException | GeneralSecurityException e) { +// log.error("Unable to decode Client public key for [{}] [{}]", store.getEndpoint(), e.getMessage()); +// } case X509: store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndpoint())); store.setSecurityMode(SecurityMode.X509); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 64498645c8..8b343ab02f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -32,7 +32,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MClientCredentials; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer; @@ -48,6 +48,7 @@ import static org.eclipse.leshan.core.SecurityMode.PSK; import static org.eclipse.leshan.core.SecurityMode.RPK; import static org.eclipse.leshan.core.SecurityMode.X509; import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP; +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; @Slf4j @Component @@ -87,25 +88,23 @@ public class LwM2mCredentialsSecurityInfoValidator { log.error("Failed to await credentials!", e); } - TbLwM2MSecurityInfo securityInfo = resultSecurityStore[0]; + return resultSecurityStore[0]; - if (securityInfo.getSecurityMode() == null) { - throw new LwM2MAuthException(); - } - - return securityInfo; +// if ((CLIENT.equals(keyValue) && securityInfo.getSecurityMode() == null) || +// (BOOTSTRAP.equals(keyValue) && securityInfo.getBootstrapCredentialConfig().getBootstrapServer()==null && securityInfo.getBootstrapCredentialConfig().getLwm2mServer()==null)){ +// throw new LwM2MAuthException(); +// } +// +// return securityInfo; } /** * Create new SecurityInfo - * @param endpoint - - * @param jsonStr - - * @param keyValue - * @return SecurityInfo */ private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTypeServer keyValue) { TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo(); - LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class); + LwM2MClientCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MClientCredentials.class); if (credentials != null) { if (keyValue.equals(BOOTSTRAP)) { result.setBootstrapCredentialConfig(credentials.getBootstrap()); @@ -114,7 +113,7 @@ public class LwM2mCredentialsSecurityInfoValidator { endpoint = StringUtils.isNotEmpty(pskClientConfig.getEndpoint()) ? pskClientConfig.getEndpoint() : endpoint; } result.setEndpoint(endpoint); - result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode()); +// result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode()); } else { result.setEndpoint(credentials.getClient().getEndpoint()); switch (credentials.getClient().getSecurityConfigClientMode()) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java index 0ef0d93f27..e3bfe986c3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java @@ -41,7 +41,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MClientCredentials; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; @@ -124,7 +124,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer } ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null; if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) { - LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class); + LwM2MClientCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MClientCredentials.class); if (!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)) { continue; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java index bc45a77b58..61774228c0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java @@ -30,9 +30,9 @@ public class TbLwM2MSecurityInfo implements Serializable { private ValidateDeviceCredentialsResponse msg; private SecurityInfo securityInfo; private SecurityMode securityMode; + private DeviceProfile deviceProfile; /** bootstrap */ - private DeviceProfile deviceProfile; private LwM2MBootstrapConfig bootstrapCredentialConfig; private String endpoint; private BootstrapConfig bootstrapConfig; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MCredentials.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MClientCredentials.java similarity index 96% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MCredentials.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MClientCredentials.java index 878188bffc..a10e5ec231 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MCredentials.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MClientCredentials.java @@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCr import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; @Data -public class LwM2MCredentials { +public class LwM2MClientCredentials { private LwM2MClientCredential client; private LwM2MBootstrapConfig bootstrap; } From ff1d86e1b0d65b32af8605d1588c9a0e8d9c0717 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 11 Nov 2021 15:09:34 +0200 Subject: [PATCH 2/6] lwm2m - upDate lwm2mServer --- ...2mDeviceProfileTransportConfiguration.java | 6 +- .../LwM2MBootstrapServersConfiguration.java | 2 +- .../LwM2MTransportBootstrapService.java | 9 +- .../secure/LwM2MBootstrapConfig.java | 88 +++++++++------ .../LwM2mDefaultBootstrapSessionManager.java | 2 + .../LwM2MBootstrapSecurityStore.java | 105 ++++++++++-------- .../LwM2MInMemoryBootstrapConfigStore.java | 2 +- ...LwM2mCredentialsSecurityInfoValidator.java | 79 +++++++------ .../lwm2m/secure/TbLwM2MSecurityInfo.java | 5 +- .../server/store/TbLwM2mSecurityStore.java | 8 +- .../lwm2m/utils/LwM2MTransportUtil.java | 4 +- .../dao/device/DeviceProfileServiceImpl.java | 5 +- 12 files changed, 175 insertions(+), 140 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/{secure => store}/LwM2MBootstrapSecurityStore.java (70%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/{secure => store}/LwM2MInMemoryBootstrapConfigStore.java (97%) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java index 1bc6ce8004..0c1441f031 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java @@ -17,17 +17,19 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration; import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingConfiguration; +import java.util.List; + @Data public class Lwm2mDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { private static final long serialVersionUID = 6257277825459600068L; private TelemetryMappingConfiguration observeAttr; - private LwM2MBootstrapServersConfiguration bootstrap; + private List bootstrap; private OtherConfiguration clientLwM2mSettings; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java index 15d573b50e..0ff1dc2eb5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java @@ -22,6 +22,6 @@ import java.util.List; @Data public class LwM2MBootstrapServersConfiguration { - List serverConfiguration; + List bootstrap; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java index 3e36d86e8e..ee14a9265d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.bootstrap; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; @@ -26,8 +25,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.config.ssl.SslCredentials; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; +import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; +import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -35,10 +34,6 @@ import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportServic import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.security.cert.X509Certificate; import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java index 2340267926..792cf71c67 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java @@ -16,23 +16,27 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure; import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.request.BindingMode; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.bootstrap.BootstrapConfig; +import org.thingsboard.server.common.data.device.credentials.lwm2m.AbstractLwM2MBootstrapClientCredentialWithKeys; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredential; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +@Slf4j @Data -@AllArgsConstructor public class LwM2MBootstrapConfig implements Serializable { List serverConfiguration; @@ -75,52 +79,68 @@ public class LwM2MBootstrapConfig implements Serializable { @Setter private LwM2MBootstrapClientCredential lwm2mServer; - public LwM2MBootstrapConfig(List serverConfiguration, LwM2MBootstrapClientCredential bootstrapClientCredential) { + public LwM2MBootstrapConfig(){}; + + public LwM2MBootstrapConfig(List serverConfiguration, LwM2MBootstrapClientCredential bootstrapClientServer, LwM2MBootstrapClientCredential lwm2mClientServer) { this.serverConfiguration = serverConfiguration; + this.bootstrapServer = bootstrapClientServer; + this.lwm2mServer = lwm2mClientServer; } @JsonIgnore public BootstrapConfig getLwM2MBootstrapConfig() { BootstrapConfig configBs = new BootstrapConfig(); - /* Delete old security objects */ + AtomicInteger index = new AtomicInteger(); + /** Delete old security/config objects in LwM2mDefaultBootstrapSessionManager -> initTasks */ configBs.toDelete.add("/0"); configBs.toDelete.add("/1"); - /* Server Configuration (object 1) as defined in LWM2M 1.0.x TS. */ - BootstrapConfig.ServerConfig server0 = new BootstrapConfig.ServerConfig(); -// server0.shortId = servers.getShortId(); -// server0.lifetime = servers.getLifetime(); -// server0.defaultMinPeriod = servers.getDefaultMinPeriod(); -// server0.notifIfDisabled = servers.isNotifIfDisabled(); -// server0.binding = BindingMode.parse(servers.getBinding()); - configBs.servers.put(0, server0); - /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */ -// this.bootstrapServer.setBootstrapServerIs(true); -// configBs.security.put(0, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.bootstrapServer.isBootstrapServerIs(), this.bootstrapServer.getSecurityMode(), this.bootstrapServer.getClientPublicKeyOrId(), this.bootstrapServer.getServerPublicKey(), this.bootstrapServer.getClientSecretKey(), this.bootstrapServer.getServerId())); -// /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Server instance = 1 */ -// configBs.security.put(1, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.lwm2mServer.isBootstrapServerIs(), this.lwm2mServer.getSecurityMode(), this.lwm2mServer.getClientPublicKeyOrId(), this.lwm2mServer.getServerPublicKey(), this.lwm2mServer.getClientSecretKey(), this.lwm2mServer.getServerId())); + serverConfiguration.forEach(serverCredential -> { + BootstrapConfig.ServerConfig serverConfig = new BootstrapConfig.ServerConfig(); + serverConfig.shortId = ((AbstractLwM2MBootstrapServerCredential)serverCredential).getShortServerId(); + serverConfig.lifetime = ((AbstractLwM2MBootstrapServerCredential)serverCredential).getLifetime(); + serverConfig.defaultMinPeriod = ((AbstractLwM2MBootstrapServerCredential)serverCredential).getDefaultMinPeriod(); + serverConfig.notifIfDisabled = ((AbstractLwM2MBootstrapServerCredential)serverCredential).isNotifIfDisabled(); + serverConfig.binding = BindingMode.parse(((AbstractLwM2MBootstrapServerCredential)serverCredential).getBinding()); + int k = index.get(); + configBs.servers.put(k, serverConfig); + BootstrapConfig.ServerSecurity serverSecurity = setServerSecurity((AbstractLwM2MBootstrapServerCredential)serverCredential, serverCredential.getSecurityMode()); + configBs.security.put(k, serverSecurity); + index.getAndIncrement(); + + }); return configBs; } - private BootstrapConfig.ServerSecurity setServerSecurity(String host, Integer port, String securityHost, Integer securityPort, boolean bootstrapServer, SecurityMode securityMode, String clientPublicKey, String serverPublicKey, String secretKey, int serverId) { + private BootstrapConfig.ServerSecurity setServerSecurity(AbstractLwM2MBootstrapServerCredential serverCredential, LwM2MSecurityMode securityMode) { BootstrapConfig.ServerSecurity serverSecurity = new BootstrapConfig.ServerSecurity(); - if (securityMode.equals(SecurityMode.NO_SEC)) { - serverSecurity.uri = "coap://" + host + ":" + Integer.toString(port); - } else { - serverSecurity.uri = "coaps://" + securityHost + ":" + Integer.toString(securityPort); + String serverUri = "coap://"; + byte[] publicKeyOrId = new byte[]{};; + byte[] secretKey = new byte[]{};; + serverSecurity.serverId = serverCredential.getShortServerId(); + serverSecurity.securityMode = SecurityMode.valueOf(securityMode.name()); + serverSecurity.bootstrapServer = serverCredential.isBootstrapServerIs(); + if (!LwM2MSecurityMode.NO_SEC.equals(securityMode)) { + serverUri = "coaps://"; + if (serverSecurity.bootstrapServer) { + publicKeyOrId = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.bootstrapServer).getDecodedClientPublicKeyOrId(); + secretKey = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.bootstrapServer).getDecodedClientSecretKey(); + + } else { + publicKeyOrId = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.lwm2mServer).getDecodedClientPublicKeyOrId(); + secretKey = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.lwm2mServer).getDecodedClientSecretKey(); + } + } - serverSecurity.bootstrapServer = bootstrapServer; - serverSecurity.securityMode = securityMode; - serverSecurity.publicKeyOrId = setPublicKeyOrId(clientPublicKey, securityMode); - serverSecurity.serverPublicKey = (serverPublicKey != null && !serverPublicKey.isEmpty()) ? Hex.decodeHex(serverPublicKey.toCharArray()) : new byte[]{}; - serverSecurity.secretKey = (secretKey != null && !secretKey.isEmpty()) ? Hex.decodeHex(secretKey.toCharArray()) : new byte[]{}; - serverSecurity.serverId = serverId; + serverUri += (((serverCredential.getHost().equals("0.0.0.0") ? "localhost" : serverCredential.getHost()) + ":" + serverCredential.getPort())); + log.info("serverSecurity.uri = [{}]", serverUri); + log.info("publicKeyOrId [{}]", Hex.encodeHexString(publicKeyOrId)); + log.info("secretKey [{}]", Hex.encodeHexString(secretKey)); + log.info("server [{}]", Hex.encodeHexString(serverCredential.getDecodedCServerPublicKey())); + serverSecurity.uri = serverUri; + serverSecurity.publicKeyOrId = publicKeyOrId; + serverSecurity.secretKey = secretKey; + serverSecurity.serverPublicKey = serverCredential.getDecodedCServerPublicKey(); return serverSecurity; } - - private byte[] setPublicKeyOrId(String publicKeyOrIdStr, SecurityMode securityMode) { - return (publicKeyOrIdStr == null || publicKeyOrIdStr.isEmpty()) ? new byte[]{} : - SecurityMode.PSK.equals(securityMode) ? publicKeyOrIdStr.getBytes(StandardCharsets.UTF_8) : - Hex.decodeHex(publicKeyOrIdStr.toCharArray()); - } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java index 097b69f806..4f47079bbe 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java @@ -34,6 +34,7 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; import org.eclipse.leshan.server.security.SecurityChecker; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import java.util.ArrayList; @@ -118,6 +119,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession session.setModel(modelProvider.getObjectModel(session, tasks.supportedObjects)); // set Requests to Send + log.info("tasks.requestsToSend = [{}]", tasks.requestsToSend); session.setRequests(tasks.requestsToSend); // prepare list where we will store Responses diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java similarity index 70% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java index c2c32ca79c..ee973f3a23 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.bootstrap.secure; +package org.thingsboard.server.transport.lwm2m.bootstrap.store; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.SecurityMode; -import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.core.util.SecurityUtil; import org.eclipse.leshan.server.bootstrap.BootstrapConfig; import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore; import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; @@ -26,9 +24,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; import org.eclipse.leshan.server.security.SecurityInfo; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; @@ -42,12 +41,12 @@ import java.util.Iterator; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.getBootstrapParametersFromThingsboard; @Slf4j @Service("LwM2MBootstrapSecurityStore") @@ -73,7 +72,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { public Iterator getAllByEndpoint(String endPoint) { // TODO TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP); - if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { + if (store.getBootstrapCredentialConfig() != null) { /* add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); BootstrapConfig bsConfigNew = store.getBootstrapConfig(); @@ -123,15 +122,15 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store); if (lwM2MBootstrapConfig != null) { /* Security info */ - switch (lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode()) { - /* Use RPK only */ - case PSK: +// switch (lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode()) { +// /* Use RPK only */ +// case PSK: // store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndpoint(), // lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId(), // Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientSecretKey().toCharArray()))); - store.setSecurityMode(SecurityMode.PSK); - break; - case RPK: +// store.setSecurityMode(SecurityMode.PSK); +// break; +// case RPK: // try { //// store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndpoint(), //// SecurityUtil.publicKey.decode(Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId().toCharArray())))); @@ -140,16 +139,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { // } catch (IOException | GeneralSecurityException e) { // log.error("Unable to decode Client public key for [{}] [{}]", store.getEndpoint(), e.getMessage()); // } - case X509: - store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndpoint())); - store.setSecurityMode(SecurityMode.X509); - break; - case NO_SEC: - store.setSecurityMode(SecurityMode.NO_SEC); - store.setSecurityInfo(null); - break; - default: - } +// case X509: +// store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndpoint())); +// store.setSecurityMode(SecurityMode.X509); +// break; +// case NO_SEC: +// store.setSecurityMode(SecurityMode.NO_SEC); +// store.setSecurityInfo(null); +// break; +// default: +// } BootstrapConfig bootstrapConfig = lwM2MBootstrapConfig.getLwM2MBootstrapConfig(); store.setBootstrapConfig(bootstrapConfig); } @@ -158,7 +157,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) { LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); if (lwM2MBootstrapConfig != null) { - LwM2MBootstrapServersConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); +// LwM2MBootstrapServersConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); // lwM2MBootstrapConfig.setServers(JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class)); // LwM2MServerBootstrap bootstrapServerProfile = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class); // if (SecurityMode.NO_SEC != bootstrapServerProfile.getSecurityMode() && bootstrapServerProfile != null) { @@ -170,23 +169,25 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { // profileLwm2mServer.setSecurityHost(profileLwm2mServer.getHost()); // profileLwm2mServer.setSecurityPort(profileLwm2mServer.getPort()); // } -// UUID sessionUUiD = UUID.randomUUID(); -// TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); -// bsSessions.put(store.getEndpoint(), sessionInfo); -// context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService())); -// if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile, lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer)) { + + + UUID sessionUUiD = UUID.randomUUID(); + TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); + bsSessions.put(store.getEndpoint(), sessionInfo); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService())); + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig)) { // lwM2MBootstrapConfig.setBootstrapServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile)); // lwM2MBootstrapConfig.setLwm2mServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer)); -// String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint()); -// helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); -// return lwM2MBootstrapConfig; -// } else { -// log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); -// log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); -// String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); -// helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); -// return null; -// } + String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint()); + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); + return lwM2MBootstrapConfig; + } else { + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); + log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); + String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); + return null; + } } log.error("Unable to decode Json or Certificate for [{}]", store.getEndpoint()); return null; @@ -196,15 +197,27 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { * Bootstrap security have to sync between (bootstrapServer in credential and bootstrapServer in profile) * and (lwm2mServer in credential and lwm2mServer in profile * - * @param bootstrapFromCredential - Bootstrap -> Security of bootstrapServer in credential - * @param bootstrapServerProfile - Bootstrap -> Security of bootstrapServer in profile - * @param lwm2mFromCredential - Bootstrap -> Security of lwm2mServer in credential - * @param profileLwm2mServer - Bootstrap -> Security of lwm2mServer in profile * @return false if not sync between SecurityMode of Bootstrap credential and profile */ - private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) { - return (bootstrapFromCredential.getSecurityMode().equals(bootstrapServerProfile.getSecurityMode()) && - lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode())); +// private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) { + private boolean getValidatedSecurityMode(LwM2MBootstrapConfig lwM2MBootstrapConfig) { + LwM2MSecurityMode bootstrapServerSecurityMode = lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode(); + LwM2MSecurityMode lwm2mServerSecurityMode = lwM2MBootstrapConfig.getLwm2mServer().getSecurityMode(); + AtomicBoolean validBs = new AtomicBoolean(true); + AtomicBoolean validLw = new AtomicBoolean(true); + lwM2MBootstrapConfig.getServerConfiguration().forEach(serverCredential -> { + if (((AbstractLwM2MBootstrapServerCredential)serverCredential).isBootstrapServerIs()) { + if (!bootstrapServerSecurityMode.equals(serverCredential.getSecurityMode())) { + validBs.set(false); + } + } + else { + if (!lwm2mServerSecurityMode.equals(serverCredential.getSecurityMode())) { + validLw.set(false); + } + } + }); + return validBs.get()&validLw.get(); } public TransportProtos.SessionInfoProto getSessionByEndpoint(String endpoint) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MInMemoryBootstrapConfigStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java similarity index 97% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MInMemoryBootstrapConfigStore.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java index ba3880c6bc..2fa470a58e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MInMemoryBootstrapConfigStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.bootstrap.secure; +package org.thingsboard.server.transport.lwm2m.bootstrap.store; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.request.Identity; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 8b343ab02f..f75aca52c3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -24,13 +24,14 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredential; -import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredential; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MClientCredentials; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; @@ -68,17 +69,16 @@ public class LwM2mCredentialsSecurityInfoValidator { @Override public void onSuccess(ValidateDeviceCredentialsResponse msg) { log.trace("Validated credentials: [{}] [{}]", credentialsId, msg); - String credentialsBody = msg.getCredentials(); - resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue); - resultSecurityStore[0].setMsg(msg); - resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile()); + resultSecurityStore[0] = createSecurityInfo(credentialsId, msg, keyValue); latch.countDown(); } @Override public void onError(Throwable e) { log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e); - resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null); + TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo(); + result.setEndpoint(credentialsId); + resultSecurityStore[0] = result; latch.countDown(); } }); @@ -88,50 +88,47 @@ public class LwM2mCredentialsSecurityInfoValidator { log.error("Failed to await credentials!", e); } - return resultSecurityStore[0]; - -// if ((CLIENT.equals(keyValue) && securityInfo.getSecurityMode() == null) || -// (BOOTSTRAP.equals(keyValue) && securityInfo.getBootstrapCredentialConfig().getBootstrapServer()==null && securityInfo.getBootstrapCredentialConfig().getLwm2mServer()==null)){ -// throw new LwM2MAuthException(); -// } -// -// return securityInfo; + TbLwM2MSecurityInfo securityInfo = resultSecurityStore[0]; + if ((CLIENT.equals(keyValue) && securityInfo.getSecurityMode() == null)) { + throw new LwM2MAuthException(); + } + return securityInfo; } /** * Create new SecurityInfo + * * @return SecurityInfo */ - private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTypeServer keyValue) { + private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, ValidateDeviceCredentialsResponse msg, LwM2mTypeServer keyValue) { TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo(); - LwM2MClientCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MClientCredentials.class); + LwM2MClientCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MClientCredentials.class); if (credentials != null) { + result.setMsg(msg); + result.setDeviceProfile(msg.getDeviceProfile()); + result.setEndpoint(credentials.getClient().getEndpoint()); +// if ((keyValue.equals(CLIENT))) { + switch (credentials.getClient().getSecurityConfigClientMode()) { + case NO_SEC: + createClientSecurityInfoNoSec(result); + break; + case PSK: + createClientSecurityInfoPSK(result, endpoint, credentials.getClient()); + break; + case RPK: + createClientSecurityInfoRPK(result, endpoint, credentials.getClient()); + break; + case X509: + createClientSecurityInfoX509(result, endpoint, credentials.getClient()); + break; + default: + break; + } +// } else if (keyValue.equals(BOOTSTRAP)) { - result.setBootstrapCredentialConfig(credentials.getBootstrap()); - if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) { - PSKClientCredential pskClientConfig = (PSKClientCredential) credentials.getClient(); - endpoint = StringUtils.isNotEmpty(pskClientConfig.getEndpoint()) ? pskClientConfig.getEndpoint() : endpoint; - } - result.setEndpoint(endpoint); -// result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode()); - } else { - result.setEndpoint(credentials.getClient().getEndpoint()); - switch (credentials.getClient().getSecurityConfigClientMode()) { - case NO_SEC: - createClientSecurityInfoNoSec(result); - break; - case PSK: - createClientSecurityInfoPSK(result, endpoint, credentials.getClient()); - break; - case RPK: - createClientSecurityInfoRPK(result, endpoint, credentials.getClient()); - break; - case X509: - createClientSecurityInfoX509(result, endpoint, credentials.getClient()); - break; - default: - break; - } + LwM2MBootstrapConfig bootstrapCredentialConfig = new LwM2MBootstrapConfig(((Lwm2mDeviceProfileTransportConfiguration) msg.getDeviceProfile().getProfileData().getTransportConfiguration()).getBootstrap(), + credentials.getBootstrap().getBootstrapServer(), credentials.getBootstrap().getLwm2mServer()); + result.setBootstrapCredentialConfig(bootstrapCredentialConfig); } } return result; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java index 61774228c0..fe5ede1bda 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java @@ -28,12 +28,13 @@ import java.io.Serializable; @Data public class TbLwM2MSecurityInfo implements Serializable { private ValidateDeviceCredentialsResponse msg; + private DeviceProfile deviceProfile; + private String endpoint; private SecurityInfo securityInfo; private SecurityMode securityMode; - private DeviceProfile deviceProfile; + /** bootstrap */ private LwM2MBootstrapConfig bootstrapCredentialConfig; - private String endpoint; private BootstrapConfig bootstrapConfig; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java index a289391dbf..fd7061b9d7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java @@ -22,6 +22,7 @@ import org.eclipse.leshan.server.security.SecurityInfo; import org.jetbrains.annotations.Nullable; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import java.util.Arrays; import java.util.HashSet; @@ -71,7 +72,12 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore { public SecurityInfo getByIdentity(String pskIdentity) { SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity); if (securityInfo == null) { - securityInfo = fetchAndPutSecurityInfo(pskIdentity); + try { + securityInfo = fetchAndPutSecurityInfo(pskIdentity); + } catch (LwM2MAuthException e) { + log.info("Registration failed: FORBIDDEN, endpointId: [{}]", pskIdentity); + securityInfo = SecurityInfo.newPreSharedKeyInfo(pskIdentity, pskIdentity, new byte[]{0x00}); + } } return securityInfo; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java index daa521abc3..3c7a8e523e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java @@ -39,7 +39,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.ota.OtaPackageKey; @@ -181,7 +181,7 @@ public class LwM2MTransportUtil { } } - public static LwM2MBootstrapServersConfiguration getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { + public static List getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { return toLwM2MClientProfile(deviceProfile).getBootstrap(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 5a7bf32518..22a9a00e15 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -46,7 +46,6 @@ import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; @@ -418,8 +417,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } } } else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) { - LwM2MBootstrapServersConfiguration lwM2MBootstrapServersConfiguration = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap(); - for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfiguration.getServerConfiguration()) { + List lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap(); + for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential); } } From 47267e503a58e67f3c3de7012bdc7628f1912c1a Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 11 Nov 2021 17:16:47 +0200 Subject: [PATCH 3/6] lwm2m - Security config default --- .../server/controller/Lwm2mController.java | 4 +-- .../server/service/lwm2m/LwM2MService.java | 4 +-- .../service/lwm2m/LwM2MServiceImpl.java | 10 +++---- .../bootstrap/LwM2MServerSecurityConfig.java | 4 --- .../LwM2MServerSecurityConfigDefault.java | 29 +++++++++++++++++++ 5 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 86ef420192..5870c7304c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -29,8 +29,8 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfig; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.lwm2m.LwM2MService; @@ -61,7 +61,7 @@ public class Lwm2mController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET) @ResponseBody - public LwM2MServerSecurityConfig getLwm2mBootstrapSecurityInfo( + public LwM2MServerSecurityConfigDefault getLwm2mBootstrapSecurityInfo( @ApiParam(value = IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION) @PathVariable(IS_BOOTSTRAP_SERVER) boolean bootstrapServer) throws ThingsboardException { try { diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java index e63f66f0bf..f5724e9ec0 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.service.lwm2m; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfig; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault; public interface LwM2MService { - LwM2MServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer); + LwM2MServerSecurityConfigDefault getServerSecurityInfo(boolean bootstrapServer); } diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java index 0ba57cbc00..5178cbf191 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java @@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfig; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault; import org.thingsboard.server.common.transport.config.ssl.SslCredentials; import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; @@ -36,14 +36,14 @@ public class LwM2MServiceImpl implements LwM2MService { private final LwM2MTransportBootstrapConfig bootstrapConfig; @Override - public LwM2MServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer) { - LwM2MServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig); + public LwM2MServerSecurityConfigDefault getServerSecurityInfo(boolean bootstrapServer) { + LwM2MServerSecurityConfigDefault result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig); result.setBootstrapServerIs(bootstrapServer); return result; } - private LwM2MServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) { - LwM2MServerSecurityConfig bsServ = new LwM2MServerSecurityConfig(); + private LwM2MServerSecurityConfigDefault getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) { + LwM2MServerSecurityConfigDefault bsServ = new LwM2MServerSecurityConfigDefault(); bsServ.setShortServerId(serverConfig.getId()); bsServ.setHost(serverConfig.getHost()); bsServ.setPort(serverConfig.getPort()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java index 7a43bd60ca..fcbe91743b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java @@ -34,10 +34,6 @@ public class LwM2MServerSecurityConfig { protected String host; @ApiModelProperty(position = 4, value = "Port for Lwm2m Server: 'No Security' mode: Lwm2m Server or Bootstrap Server", example = "'5685' or '5687'", readOnly = true) protected Integer port; - @ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true) - protected String securityHost; - @ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true) - protected Integer securityPort; @ApiModelProperty(position = 7, value = "Client Hold Off Time. The number of seconds to wait before initiating a Client Initiated Bootstrap once the LwM2M Client has determined it should initiate this bootstrap mode. (This information is relevant for use with a Bootstrap-Server only.)", example = "1", readOnly = true) protected Integer clientHoldOffTime = 1; @ApiModelProperty(position = 8, value = "Server Public Key for 'Security' mode (DTLS): RPK or X509. Format: base64 encoded", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java new file mode 100644 index 0000000000..2c79507810 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel +@Data +public class LwM2MServerSecurityConfigDefault extends LwM2MServerSecurityConfig { + @ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true) + protected String securityHost; + @ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true) + protected Integer securityPort; +} From 25abfcebf3486f0838876ef3fbef317cdc35fed5 Mon Sep 17 00:00:00 2001 From: Sergey Tarnavskiy Date: Mon, 15 Nov 2021 18:48:03 +0200 Subject: [PATCH 4/6] Bootstrap servers refactoring for LwM2M transport --- .../add-device-profile-dialog.component.html | 1 + .../device-profile-alarms.component.html | 4 +- .../profile/device-profile.component.html | 1 + .../profile/device-profile.component.ts | 3 + ...ile-transport-configuration.component.html | 1 + ...ofile-transport-configuration.component.ts | 3 + ...ap-add-config-server-dialog.component.html | 55 ++++ ...trap-add-config-server-dialog.component.ts | 60 ++++ ...2m-bootstrap-config-servers.component.html | 39 +++ ...wm2m-bootstrap-config-servers.component.ts | 185 +++++++++++++ .../lwm2m-device-config-server.component.html | 257 +++++++++++------- .../lwm2m-device-config-server.component.ts | 44 ++- ...ile-transport-configuration.component.html | 90 +----- ...ofile-transport-configuration.component.ts | 56 +--- .../lwm2m/lwm2m-profile-components.module.ts | 6 + .../lwm2m/lwm2m-profile-config.models.ts | 66 ++--- .../device-wizard-dialog.component.html | 1 + .../device-profile-tabs.component.html | 1 + .../device-profile-tabs.component.ts | 5 + ui-ngx/src/app/shared/models/device.models.ts | 9 +- .../models/lwm2m-security-config.models.ts | 14 +- .../assets/locale/locale.constant-en_US.json | 17 +- 22 files changed, 613 insertions(+), 305 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html index 23fdf04cde..42ef973988 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html @@ -100,6 +100,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html index c8433fde7c..df0d5af5a3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html @@ -34,9 +34,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 917ad25dd2..d96d1788c7 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -138,6 +138,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts index 5621e60418..6492cc2d1e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts @@ -67,6 +67,8 @@ export class DeviceProfileComponent extends EntityComponent { displayTransportConfiguration: boolean; + isTransportTypeChanged = false; + serviceType = ServiceType.TB_RULE_ENGINE; deviceProfileId: EntityId; @@ -158,6 +160,7 @@ export class DeviceProfileComponent extends EntityComponent { const deviceTransportType: DeviceTransportType = form.get('transportType').value; this.displayTransportConfiguration = deviceTransportType && deviceTransportTypeConfigurationInfoMap.get(deviceTransportType).hasProfileConfiguration; + this.isTransportTypeChanged = true; let profileData: DeviceProfileData = form.getRawValue().profileData; if (!profileData) { profileData = { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html index f8fb2c7baa..87c222216c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html @@ -37,6 +37,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts index 7d74cbc387..c889fc7eb8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts @@ -50,6 +50,9 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu @Input() disabled: boolean; + @Input() + isAdd: boolean; + transportType: DeviceTransportType; private propagateChange = (v: any) => { }; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.html new file mode 100644 index 0000000000..34441d55f5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.html @@ -0,0 +1,55 @@ + +
+ +

device-profile.lwm2m.add-new-server-title

+ + +
+ + +
+
+ + {{ 'device-profile.lwm2m.server-type' | translate }} + + + {{ serverConfigTypeNamesMap.get(serverType) | translate }} + + + +
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.ts new file mode 100644 index 0000000000..55fcc7d751 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.ts @@ -0,0 +1,60 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MatDialogRef } from '@angular/material/dialog'; +import { + ServerConfigType, + ServerConfigTypeTranslationMap +} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; + +@Component({ + selector: 'tb-profile-lwm2m-bootstrap-add-config-server-dialog', + templateUrl: './lwm2m-bootstrap-add-config-server-dialog.component.html' +}) +export class Lwm2mBootstrapAddConfigServerDialogComponent extends DialogComponent { + + addConfigServerFormGroup: FormGroup; + + serverTypes = Object.values(ServerConfigType); + serverConfigTypeNamesMap = ServerConfigTypeTranslationMap; + + constructor(protected store: Store, + protected router: Router, + private fb: FormBuilder, + public dialogRef: MatDialogRef, + ) { + super(store, router, dialogRef); + this.addConfigServerFormGroup = this.fb.group({ + serverType: [ServerConfigType.LWM2M] + }); + } + + addServerConfig() { + this.dialogRef.close(this.addConfigServerFormGroup.get('serverType').value === ServerConfigType.BOOTSTRAP); + } + + cancel(): void { + this.dialogRef.close(null); + } +} + + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html new file mode 100644 index 0000000000..cd00d1661a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html @@ -0,0 +1,39 @@ + +
+ +
+ + +
+
+
+ device-profile.lwm2m.no-config-servers +
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts new file mode 100644 index 0000000000..a4bf5d4958 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts @@ -0,0 +1,185 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR +} from '@angular/forms'; +import { of, Subscription } from 'rxjs'; +import { ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DialogService } from '@core/services/dialog.service'; +import { MatDialog } from '@angular/material/dialog'; +import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component'; +import { mergeMap } from 'rxjs/operators'; +import { DeviceProfileService } from '@core/http/device-profile.service'; +import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; + +@Component({ + selector: 'tb-profile-lwm2m-bootstrap-config-servers', + templateUrl: './lwm2m-bootstrap-config-servers.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => Lwm2mBootstrapConfigServersComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Lwm2mBootstrapConfigServersComponent), + multi: true, + } + ] +}) +export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValueAccessor { + + bootstrapConfigServersFormGroup: FormGroup; + + @Input() + disabled: boolean; + + private valueChangeSubscription: Subscription = null; + + private propagateChange = (v: any) => { }; + + constructor(public translate: TranslateService, + public matDialog: MatDialog, + private dialogService: DialogService, + private deviceProfileService: DeviceProfileService, + private fb: FormBuilder) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.bootstrapConfigServersFormGroup = this.fb.group({ + serverConfigs: this.fb.array([]) + }); + } + + serverConfigsFromArray(): FormArray { + return this.bootstrapConfigServersFormGroup.get('serverConfigs') as FormArray; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.bootstrapConfigServersFormGroup.disable({emitEvent: false}); + } else { + this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); + } + } + + writeValue(serverConfigs: Array | null): void { + if (this.valueChangeSubscription) { + this.valueChangeSubscription.unsubscribe(); + } + const serverConfigsControls: Array = []; + if (serverConfigs) { + serverConfigs.forEach((serverConfig) => { + serverConfigsControls.push(this.fb.control(serverConfig)); + }); + } + this.bootstrapConfigServersFormGroup.setControl('serverConfigs', this.fb.array(serverConfigsControls)); + if (this.disabled) { + this.bootstrapConfigServersFormGroup.disable({emitEvent: false}); + } else { + this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); + } + this.valueChangeSubscription = this.bootstrapConfigServersFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + trackByParams(index: number): number { + return index; + } + + removeServerConfig($event: Event, index: number) { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + this.dialogService.confirm( + this.translate.instant('device-profile.lwm2m.delete-server-title'), + this.translate.instant('device-profile.lwm2m.delete-server-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes'), + true + ).subscribe((result) => { + if (result) { + this.serverConfigsFromArray().removeAt(index); + } + }); + } + + addServerConfig(): void { + const addDialogObs = this.isBootstrapAdded() ? of(false) : + this.matDialog.open(Lwm2mBootstrapAddConfigServerDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed(); + const addServerConfigObs = addDialogObs.pipe( + mergeMap((isBootstrap) => { + if (isBootstrap === null) { + return of(null); + } + return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(isBootstrap, Lwm2mSecurityType.NO_SEC); + }) + ); + addServerConfigObs.subscribe((serverConfig) => { + if (serverConfig) { + serverConfig.securityMode = Lwm2mSecurityType.NO_SEC; + this.serverConfigsFromArray().push(this.fb.control(serverConfig)); + this.updateModel(); + } + }); + } + + public validate(c: FormControl) { + return (this.bootstrapConfigServersFormGroup.valid) ? null : { + serverConfigs: { + valid: false, + }, + }; + } + + public isBootstrapAdded() { + const serverConfigsArray = this.serverConfigsFromArray().getRawValue(); + for (let i = 0; i < serverConfigsArray.length; i++) { + if (serverConfigsArray[i].bootstrapServerIs) { + return true; + } + } + return false; + } + + private updateModel() { + const serverConfigs: Array = this.serverConfigsFromArray().value; + this.propagateChange(serverConfigs); + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index 3fe4e7cda8..4019c7bf65 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -15,99 +15,170 @@ limitations under the License. --> -
-
- - {{ 'device-profile.lwm2m.mode' | translate }} - - - {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} - - - - - {{ 'device-profile.lwm2m.server-host' | translate }} - - - {{ 'device-profile.lwm2m.server-host-required' | translate }} - - - - {{ 'device-profile.lwm2m.server-port' | translate }} - - - {{ 'device-profile.lwm2m.server-port-required' | translate }} - - - {{ 'device-profile.lwm2m.server-port-pattern' | translate }} - - + +
+ +
+
{{ (serverFormGroup.get('bootstrapServerIs').value ? + 'device-profile.lwm2m.bootstrap-server' : 'device-profile.lwm2m.lwm2m-server') | translate }}
+
+
{{ ('device-profile.lwm2m.short-id' | translate) + ': ' }} + {{ serverFormGroup.get('shortServerId').value }} +
+
{{ ('device-profile.lwm2m.mode' | translate) + ': ' }} + {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[serverFormGroup.get('securityMode').value]) }} +
+
+
+
+ + +
+
+ +
+
+ + {{ 'device-profile.lwm2m.mode' | translate }} + + + {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} + + + + + {{ 'device-profile.lwm2m.short-id' | translate }} + help + + + {{ 'device-profile.lwm2m.short-id-required' | translate }} + + + {{ 'device-profile.lwm2m.short-id-pattern' | translate }} + + + {{ 'device-profile.lwm2m.short-id-range' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.server-host' | translate }} + + + {{ 'device-profile.lwm2m.server-host-required' | translate }} + + + + {{ 'device-profile.lwm2m.server-port' | translate }} + + + {{ 'device-profile.lwm2m.server-port-required' | translate }} + + + {{ 'device-profile.lwm2m.server-port-pattern' | translate }} + + - {{ 'device-profile.lwm2m.server-port-range' | translate }} - - -
-
- - {{ 'device-profile.lwm2m.short-id' | translate }} - help - - - {{ 'device-profile.lwm2m.short-id-required' | translate }} - - - {{ 'device-profile.lwm2m.short-id-pattern' | translate }} - - - {{ 'device-profile.lwm2m.short-id-range' | translate }} - - - - {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} - help - - - {{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }} - - + + {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} + help + + + {{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }} + + - {{ 'device-profile.lwm2m.client-hold-off-time-pattern' | translate }} - - - - {{ 'device-profile.lwm2m.account-after-timeout' | translate }} - help - - - {{ 'device-profile.lwm2m.account-after-timeout-required' | translate }} - - help + + + {{ 'device-profile.lwm2m.account-after-timeout-required' | translate }} + + - {{ 'device-profile.lwm2m.account-after-timeout-pattern' | translate }} - - -
-
- - {{ 'device-profile.lwm2m.server-public-key' | translate }} - help - - - {{ 'device-profile.lwm2m.server-public-key-required' | translate }} - - -
-
+ {{ 'device-profile.lwm2m.account-after-timeout-pattern' | translate }} +
+
+
+
+ + {{ 'device-profile.lwm2m.server-public-key' | translate }} + help + + + {{ 'device-profile.lwm2m.server-public-key-required' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.lifetime' | translate }} + + + {{ 'device-profile.lwm2m.lifetime-required' | translate }} + + + {{ 'device-profile.lwm2m.lifetime-pattern' | translate }} + + + + {{ 'device-profile.lwm2m.default-min-period' | translate }} + help + + + {{ 'device-profile.lwm2m.default-min-period-required' | translate }} + + + {{ 'device-profile.lwm2m.default-min-period-pattern' | translate }} + + +
+ + {{ 'device-profile.lwm2m.binding' | translate }} + help + + + {{ bindingModeTypeNamesMap.get(bindingMode) | translate }} + + + + + {{ 'device-profile.lwm2m.notification-storing' | translate }} + +
+
+ diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index 9492b18114..c92cf9ef45 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -26,8 +26,7 @@ import { Validators } from '@angular/forms'; import { - DEFAULT_PORT_BOOTSTRAP_NO_SEC, - DEFAULT_PORT_SERVER_NO_SEC, + BingingMode, BingingModeTranslationsMap, ServerSecurityConfig } from './lwm2m-profile-config.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; @@ -65,14 +64,16 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc private isDataLoadedIntoCache = false; serverFormGroup: FormGroup; + bindingModeTypes = Object.values(BingingMode); + bindingModeTypeNamesMap = BingingModeTranslationsMap; securityConfigLwM2MType = Lwm2mSecurityType; securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType); credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap; publicKeyOrIdTooltipNamesMap = Lwm2mPublicKeyOrIdTooltipTranslationsMap; currentSecurityMode = null; - @Input() - isBootstrapServer = false; + @Output() + removeServer = new EventEmitter(); private propagateChange = (v: any) => { }; @@ -83,19 +84,29 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc ngOnInit(): void { this.serverFormGroup = this.fb.group({ host: ['', Validators.required], - port: [this.isBootstrapServer ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC, - [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]], + port: ['', [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]], securityMode: [Lwm2mSecurityType.NO_SEC], serverPublicKey: [''], clientHoldOffTime: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], - serverId: ['', [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]], + shortServerId: ['', [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]], bootstrapServerAccountTimeout: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + binding: [''], + lifetime: [null, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + notifIfDisabled: [], + defaultMinPeriod: [null, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + bootstrapServerIs: [] }); this.serverFormGroup.get('securityMode').valueChanges.pipe( - tap(securityMode => this.updateValidate(securityMode)), + tap((securityMode) => { + this.currentSecurityMode = securityMode; + this.updateValidate(securityMode); + }), mergeMap(securityMode => this.getLwm2mBootstrapSecurityInfo(securityMode)), takeUntil(this.destroy$) ).subscribe(serverSecurityConfig => { + if (this.currentSecurityMode !== Lwm2mSecurityType.NO_SEC) { + this.changeSecurityHostPortFields(serverSecurityConfig); + } this.serverFormGroup.patchValue(serverSecurityConfig, {emitEvent: false}); }); this.serverFormGroup.valueChanges.pipe( @@ -114,8 +125,13 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc if (serverData) { this.serverFormGroup.patchValue(serverData, {emitEvent: false}); this.updateValidate(serverData.securityMode); + if (serverData.securityHost && serverData.securityPort) { + delete serverData.securityHost; + delete serverData.securityPort; + this.propagateChangeState(this.serverFormGroup.getRawValue()); + } } - if (!this.isDataLoadedIntoCache){ + if (!this.isDataLoadedIntoCache) { this.getLwm2mBootstrapSecurityInfo().subscribe(value => { if (!serverData) { this.serverFormGroup.patchValue(value); @@ -171,11 +187,17 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } private getLwm2mBootstrapSecurityInfo(securityMode = Lwm2mSecurityType.NO_SEC): Observable { - return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(this.isBootstrapServer, securityMode).pipe( + return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType( + this.serverFormGroup.get('bootstrapServerIs').value, securityMode).pipe( tap(() => this.isDataLoadedIntoCache = true) ); } + private changeSecurityHostPortFields(serverData: ServerSecurityConfig): void { + serverData.port = serverData.securityPort; + serverData.host = serverData.securityHost; + } + validate(): ValidationErrors | null { return this.serverFormGroup.valid ? null : { serverFormGroup: true diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index a042ba5ea3..20cbd63168 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -33,93 +33,9 @@
-
- - - - {{ 'device-profile.lwm2m.server' | translate }} - - -
- - {{ 'device-profile.lwm2m.short-id' | translate }} - help - - - {{ 'device-profile.lwm2m.short-id-required' | translate }} - - - {{ 'device-profile.lwm2m.short-id-range' | translate }} - - - {{ 'device-profile.lwm2m.short-id-pattern' | translate }} - - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - - - {{ 'device-profile.lwm2m.lifetime-required' | translate }} - - - {{ 'device-profile.lwm2m.lifetime-pattern' | translate }} - - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - help - - - {{ 'device-profile.lwm2m.default-min-period-required' | translate }} - - - {{ 'device-profile.lwm2m.default-min-period-pattern' | translate }} - - -
- - {{ 'device-profile.lwm2m.binding' | translate }} - help - - - {{ bindingModeTypeNamesMap.get(bindingMode) | translate }} - - - - - {{ 'device-profile.lwm2m.notification-storing' | translate }} - -
-
- - - {{ 'device-profile.lwm2m.bootstrap-server' | translate }} - - - - - - - - - {{ 'device-profile.lwm2m.lwm2m-server' | translate }} - - - - - - -
+
+ +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 792aa3c315..1fdf6cf8be 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -28,20 +28,11 @@ import { import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ATTRIBUTE, - BingingMode, - BingingModeTranslationsMap, - DEFAULT_BINDING, DEFAULT_EDRX_CYCLE, DEFAULT_FW_UPDATE_RESOURCE, - DEFAULT_ID_SERVER, - DEFAULT_LIFE_TIME, - DEFAULT_MIN_PERIOD, - DEFAULT_NOTIF_IF_DESIBLED, DEFAULT_PAGING_TRANSMISSION_WINDOW, DEFAULT_PSM_ACTIVITY_TIMER, DEFAULT_SW_UPDATE_RESOURCE, - getDefaultBootstrapServerSecurityConfig, - getDefaultLwM2MServerSecurityConfig, Instance, INSTANCES, KEY_NAME, @@ -60,6 +51,7 @@ import { Direction } from '@shared/models/page/sort-order'; import _ from 'lodash'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', @@ -83,8 +75,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private requiredValue: boolean; private destroy$ = new Subject(); - bindingModeTypes = Object.values(BingingMode); - bindingModeTypeNamesMap = BingingModeTranslationsMap; lwm2mDeviceProfileFormGroup: FormGroup; configurationValue: Lwm2mProfileConfigModels; sortFunction: (key: string, value: object) => object; @@ -98,6 +88,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } + @Input() + isAdd: boolean; + private propagateChange = (v: any) => { } @@ -106,17 +99,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.lwm2mDeviceProfileFormGroup = this.fb.group({ objectIds: [null], observeAttrTelemetry: [null], - bootstrap: this.fb.group({ - servers: this.fb.group({ - binding: [DEFAULT_BINDING], - shortId: [DEFAULT_ID_SERVER, [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]], - lifetime: [DEFAULT_LIFE_TIME, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], - notifIfDisabled: [DEFAULT_NOTIF_IF_DESIBLED, []], - defaultMinPeriod: [DEFAULT_MIN_PERIOD, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], - }), - bootstrapServer: [null, Validators.required], - lwm2mServer: [null, Validators.required] - }), + bootstrap: [[]], clientLwM2mSettings: this.fb.group({ clientOnlyObserveAfterConnect: [1, []], fwUpdateStrategy: [1, []], @@ -187,9 +170,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro async writeValue(value: Lwm2mProfileConfigModels | null) { if (isDefinedAndNotNull(value) && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { this.configurationValue = value; - const defaultFormSettings = value.clientLwM2mSettings.fwUpdateStrategy === 1 && - isUndefined(value.clientLwM2mSettings.fwUpdateResource); - if (defaultFormSettings) { + if (this.isAdd) { await this.defaultProfileConfig(); } this.initWriteValue(); @@ -203,23 +184,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private async defaultProfileConfig(): Promise { - let bootstrap: ServerSecurityConfig; - let lwm2m: ServerSecurityConfig; - try { - [bootstrap, lwm2m] = await Promise.all([ - this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(true).toPromise(), - this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(false).toPromise() - ]); - } catch (e) { - bootstrap = getDefaultBootstrapServerSecurityConfig(); - lwm2m = getDefaultLwM2MServerSecurityConfig(); + const lwm2m: ServerSecurityConfig = await this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(false).toPromise(); + if (lwm2m) { + lwm2m.securityMode = Lwm2mSecurityType.NO_SEC; } - - this.configurationValue.bootstrap.bootstrapServer = bootstrap; - this.configurationValue.bootstrap.lwm2mServer = lwm2m; + this.configurationValue.bootstrap = [lwm2m]; this.lwm2mDeviceProfileFormGroup.patchValue({ bootstrap: this.configurationValue.bootstrap - }, {emitEvent: false}); + }, {emitEvent: true}); } private initWriteValue = (): void => { @@ -282,12 +254,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateDeviceProfileValue(config): void { - if (this.lwm2mDeviceProfileFormGroup.valid) { + if (this.lwm2mDeviceProfileFormGroup.valid && config.observeAttrTelemetry) { this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry); } - this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer; - this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer; - this.configurationValue.bootstrap.servers = config.bootstrap.servers; + this.configurationValue.bootstrap = config.bootstrap; this.configurationValue.clientLwM2mSettings = config.clientLwM2mSettings; this.updateModel(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts index aa801f84dc..77ef7ee81e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts @@ -22,7 +22,9 @@ import { Lwm2mObserveAttrTelemetryResourcesComponent } from './lwm2m-observe-att import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component'; import { Lwm2mAttributesComponent } from './lwm2m-attributes.component'; import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component'; +import { Lwm2mBootstrapConfigServersComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component'; import { Lwm2mDeviceConfigServerComponent } from './lwm2m-device-config-server.component'; +import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component'; import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-instances-dialog.component'; import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component'; import { CommonModule } from '@angular/common'; @@ -40,7 +42,9 @@ import { DeviceProfileCommonModule } from '@home/components/profile/device/commo Lwm2mAttributesDialogComponent, Lwm2mAttributesComponent, Lwm2mAttributesKeyListComponent, + Lwm2mBootstrapConfigServersComponent, Lwm2mDeviceConfigServerComponent, + Lwm2mBootstrapAddConfigServerDialogComponent, Lwm2mObjectAddInstancesDialogComponent, Lwm2mObjectAddInstancesListComponent, Lwm2mObserveAttrTelemetryInstancesComponent @@ -58,7 +62,9 @@ import { DeviceProfileCommonModule } from '@home/components/profile/device/commo Lwm2mAttributesDialogComponent, Lwm2mAttributesComponent, Lwm2mAttributesKeyListComponent, + Lwm2mBootstrapConfigServersComponent, Lwm2mDeviceConfigServerComponent, + Lwm2mBootstrapAddConfigServerDialogComponent, Lwm2mObjectAddInstancesDialogComponent, Lwm2mObjectAddInstancesListComponent, Lwm2mObserveAttrTelemetryInstancesComponent diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index e95d1d22d2..543c000e12 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -96,6 +96,18 @@ export const AttributeNameTranslationMap = new Map( ] ); +export enum ServerConfigType { + LWM2M = 'LWM2M', + BOOTSTRAP = 'BOOTSTRAP' +} + +export const ServerConfigTypeTranslationMap = new Map( + [ + [ServerConfigType.LWM2M, 'device-profile.lwm2m.lwm2m-server'], + [ServerConfigType.BOOTSTRAP, 'device-profile.lwm2m.bootstrap-server'] + ] +); + export enum PowerMode { PSM = 'PSM', DRX = 'DRX', @@ -110,22 +122,21 @@ export const PowerModeTranslationMap = new Map( ] ); -export interface BootstrapServersSecurityConfig { - shortId: number; - lifetime: number; - defaultMinPeriod: number; - notifIfDisabled: boolean; - binding: string; -} - export interface ServerSecurityConfig { host?: string; port?: number; securityMode: Lwm2mSecurityType; + securityHost?: string; + securityPort?: number; serverPublicKey?: string; clientHoldOffTime?: number; - serverId?: number; + shortServerId?: number; bootstrapServerAccountTimeout: number; + lifetime: number; + defaultMinPeriod: number; + notifIfDisabled: boolean; + binding: string; + bootstrapServerIs: boolean; } export interface ServerSecurityConfigInfo extends ServerSecurityConfig { @@ -134,16 +145,10 @@ export interface ServerSecurityConfigInfo extends ServerSecurityConfig { bootstrapServerIs: boolean; } -interface BootstrapSecurityConfig { - servers: BootstrapServersSecurityConfig; - bootstrapServer: ServerSecurityConfig; - lwm2mServer: ServerSecurityConfig; -} - export interface Lwm2mProfileConfigModels { clientLwM2mSettings: ClientLwM2mSettings; observeAttr: ObservableAttributes; - bootstrap: BootstrapSecurityConfig; + bootstrap: Array; } export interface ClientLwM2mSettings { @@ -167,35 +172,6 @@ export interface ObservableAttributes { attributeLwm2m: AttributesNameValueMap; } -export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { - return { - shortId: DEFAULT_ID_SERVER, - lifetime: DEFAULT_LIFE_TIME, - defaultMinPeriod: DEFAULT_MIN_PERIOD, - notifIfDisabled: DEFAULT_NOTIF_IF_DESIBLED, - binding: DEFAULT_BINDING - }; -} - -export function getDefaultBootstrapServerSecurityConfig(): ServerSecurityConfig { - return { - bootstrapServerAccountTimeout: DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT, - clientHoldOffTime: DEFAULT_CLIENT_HOLD_OFF_TIME, - host: DEFAULT_LOCAL_HOST_NAME, - port: DEFAULT_PORT_BOOTSTRAP_NO_SEC, - securityMode: Lwm2mSecurityType.NO_SEC, - serverId: DEFAULT_ID_BOOTSTRAP, - serverPublicKey: '' - }; -} - -export function getDefaultLwM2MServerSecurityConfig(): ServerSecurityConfig { - const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig(); - DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC; - DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER; - return DefaultLwM2MServerSecurityConfig; -} - export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { return { observe: [], diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index ae0be158d5..6a84e626d9 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -133,6 +133,7 @@ diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html index f1a7af1e46..a0ce99f3db 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html @@ -35,6 +35,7 @@
diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts index 2c53f04986..f1afa2559a 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts @@ -38,12 +38,17 @@ export class DeviceProfileTabsComponent extends EntityTabsComponent) { super(store); } ngOnInit() { super.ngOnInit(); + this.detailsForm.get('transportType').valueChanges.subscribe(() => { + this.isTransportTypeChanged = true; + }); } } diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 7ebf7bde47..7fc3d70f78 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -31,9 +31,6 @@ import { OtaPackageId } from '@shared/models/id/ota-package-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; import { DataType } from '@shared/models/constants'; import { - getDefaultBootstrapServerSecurityConfig, - getDefaultBootstrapServersSecurityConfig, - getDefaultLwM2MServerSecurityConfig, getDefaultProfileClientLwM2mSettingsConfig, getDefaultProfileObserveAttrConfig, PowerMode @@ -384,11 +381,7 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT case DeviceTransportType.LWM2M: const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = { observeAttr: getDefaultProfileObserveAttrConfig(), - bootstrap: { - servers: getDefaultBootstrapServersSecurityConfig(), - bootstrapServer: getDefaultBootstrapServerSecurityConfig(), - lwm2mServer: getDefaultLwM2MServerSecurityConfig() - }, + bootstrap: [], clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig() }; transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M}; diff --git a/ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts b/ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts index 024433b0c7..3187f0661c 100644 --- a/ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts +++ b/ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts @@ -67,14 +67,9 @@ export interface ServerSecurityConfig { clientSecretKey?: string; } -interface BootstrapSecurityConfig { - bootstrapServer: ServerSecurityConfig; - lwm2mServer: ServerSecurityConfig; -} - export interface Lwm2mSecurityConfigModels { client: ClientSecurityConfig; - bootstrap: BootstrapSecurityConfig; + bootstrap: Array; } @@ -84,10 +79,9 @@ export function getLwm2mSecurityConfigModelsDefault(): Lwm2mSecurityConfigModels securityConfigClientMode: Lwm2mSecurityType.NO_SEC, endpoint: '' }, - bootstrap: { - bootstrapServer: getDefaultServerSecurityConfig(), - lwm2mServer: getDefaultServerSecurityConfig() - } + bootstrap: [ + getDefaultServerSecurityConfig() + ] }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index dcc599bc41..47ad830c33 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1310,16 +1310,18 @@ "edit-attribute": "Edit attribute", "view-attribute": "View attribute", "remove-attribute": "Remove attribute", + "delete-server-text": "Be careful, after the confirmation the server configuration will become unrecoverable.", + "delete-server-title": "Are you sure you want to delete the server?", "mode": "Security config mode", "bootstrap-tab": "Bootstrap", "bootstrap-server-legend": "Bootstrap Server (ShortId...)", "lwm2m-server-legend": "LwM2M Server (ShortId...)", "server": "Server", - "short-id": "Short ID", - "short-id-tooltip": "Server ShortID must be equal Security ShortID ", - "short-id-required": "Short ID is required.", - "short-id-range": "Short ID should be in a range from 1 to 65534.", - "short-id-pattern": "Short ID must be a positive integer.", + "short-id": "Short server ID", + "short-id-tooltip": "Server short Id. Used as link to associate server Object Instance.\nThis identifier uniquely identifies each LwM2M Server configured for the LwM2M Client.\nResource MUST be set when the Bootstrap-Server Resource has a value of 'false'.\nThe values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.", + "short-id-required": "Short server ID is required.", + "short-id-range": "Short server ID should be in a range from 1 to 65534.", + "short-id-pattern": "Short server ID must be a positive integer.", "lifetime": "Client registration lifetime", "lifetime-required": "Client registration lifetime is required.", "lifetime-pattern": "Client registration lifetime must be a positive integer.", @@ -1361,6 +1363,11 @@ "account-after-timeout-required": "Account after the timeout is required.", "account-after-timeout-pattern": "Account after the timeout must be a positive integer.", "account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.", + "server-type": "Server type", + "add-new-server-title": "Add new server config", + "add-server-config": "Add server config", + "add-lwm2m-server-config": "Add LwM2M server", + "no-config-servers": "No servers configured", "others-tab": "Other settings", "client-strategy": "Client strategy when connecting", "client-strategy-label": "Strategy", From cf5477ebf9a21a3458d2038dec5b3c2705a33fa4 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 15 Nov 2021 19:11:53 +0200 Subject: [PATCH 5/6] lwm2m - Bootstrap auto --- .../bootstrap/LwM2MServerSecurityConfig.java | 17 +- .../secure/LwM2MBootstrapConfig.java | 89 ++++--- .../LwM2mDefaultBootstrapSessionManager.java | 24 +- ...LwM2MBootstrapConfigStoreTaskProvider.java | 242 ++++++++++++++++++ .../store/LwM2MBootstrapSecurityStore.java | 45 +--- .../device/DeviceCredentialsServiceImpl.java | 7 +- 6 files changed, 313 insertions(+), 111 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java index fcbe91743b..12dd9c5a64 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java @@ -23,12 +23,15 @@ import lombok.Data; @Data public class LwM2MServerSecurityConfig { - @ApiModelProperty(position = 1, value = "Server short Id. Used as link to associate server Object Instance.\nThis identifier uniquely identifies each LwM2M Server configured for the LwM2M Client.\n" + - "This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. \n" + + @ApiModelProperty(position = 1, value = "Server short Id. Used as link to associate server Object Instance. This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client. " + + "This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. " + "The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.", example = "123", readOnly = true) protected Integer shortServerId = 123; /** Security -> ObjectId = 0 'LWM2M Security' */ - @ApiModelProperty(position = 2, value = "Is Bootstrap Server or Lwm2m Server", example = "true or false", readOnly = true) + @ApiModelProperty(position = 2, value = "Is Bootstrap Server or Lwm2m Server. " + + "The LwM2M Client MAY be configured to use one or more LwM2M Server Account(s). " + + "The LwM2M Client MUST have at most one LwM2M Bootstrap-Server Account. " + + "(*) The LwM2M client MUST have at least one LwM2M server account after completing the boot sequence specified.", example = "true or false", readOnly = true) protected boolean bootstrapServerIs = false; @ApiModelProperty(position = 3, value = "Host for 'No Security' mode", example = "0.0.0.0", readOnly = true) protected String host; @@ -45,15 +48,15 @@ public class LwM2MServerSecurityConfig { /** Config -> ObjectId = 1 'LwM2M Server' */ @ApiModelProperty(position = 10, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true) private Integer lifetime = 300; - @ApiModelProperty(position = 11, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation.\n" + + @ApiModelProperty(position = 11, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " + "If this Resource doesn’t exist, the default value is 0.", example = "1", readOnly = true) private Integer defaultMinPeriod = 1; /** ResourceID=6 'Notification Storing When Disabled or Offline' */ - @ApiModelProperty(position = 12, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server.\n" + - "If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline.\n" + + @ApiModelProperty(position = 12, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " + + "If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline. " + "The default value is true.", example = "true", readOnly = true) private boolean notifIfDisabled = true; - @ApiModelProperty(position = 14, value = "This Resource defines the transport binding configured for the LwM2M Client.\n" + + @ApiModelProperty(position = 14, value = "This Resource defines the transport binding configured for the LwM2M Client. " + "If the LwM2M Client supports the binding specified in this Resource, the LwM2M Client MUST use that transport for the Current Binding Mode.", example = "U", readOnly = true) private String binding = "U"; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java index 792cf71c67..124de67fa0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java @@ -40,23 +40,6 @@ import java.util.concurrent.atomic.AtomicInteger; public class LwM2MBootstrapConfig implements Serializable { List serverConfiguration; - /* - interface BootstrapSecurityConfig - servers: BootstrapServersSecurityConfig, - bootstrapServer: ServerSecurityConfig, - lwm2mServer: ServerSecurityConfig - } - */ - /** -servers - * shortId: number, - * lifetime: number, - * defaultMinPeriod: number, - * notifIfDisabled: boolean, - * binding: string - * */ -// @Getter -// @Setter -// private LwM2MBootstrapServers servers; /** -bootstrapServer, lwm2mServer * interface ServerSecurityConfig @@ -91,56 +74,70 @@ public class LwM2MBootstrapConfig implements Serializable { @JsonIgnore public BootstrapConfig getLwM2MBootstrapConfig() { BootstrapConfig configBs = new BootstrapConfig(); - AtomicInteger index = new AtomicInteger(); - /** Delete old security/config objects in LwM2mDefaultBootstrapSessionManager -> initTasks */ - configBs.toDelete.add("/0"); - configBs.toDelete.add("/1"); - serverConfiguration.forEach(serverCredential -> { - BootstrapConfig.ServerConfig serverConfig = new BootstrapConfig.ServerConfig(); - serverConfig.shortId = ((AbstractLwM2MBootstrapServerCredential)serverCredential).getShortServerId(); - serverConfig.lifetime = ((AbstractLwM2MBootstrapServerCredential)serverCredential).getLifetime(); - serverConfig.defaultMinPeriod = ((AbstractLwM2MBootstrapServerCredential)serverCredential).getDefaultMinPeriod(); - serverConfig.notifIfDisabled = ((AbstractLwM2MBootstrapServerCredential)serverCredential).isNotifIfDisabled(); - serverConfig.binding = BindingMode.parse(((AbstractLwM2MBootstrapServerCredential)serverCredential).getBinding()); - int k = index.get(); - configBs.servers.put(k, serverConfig); - BootstrapConfig.ServerSecurity serverSecurity = setServerSecurity((AbstractLwM2MBootstrapServerCredential)serverCredential, serverCredential.getSecurityMode()); - configBs.security.put(k, serverSecurity); - index.getAndIncrement(); + configBs.autoIdForSecurityObject = true; + int id = 0; + for (LwM2MBootstrapServerCredential serverCredential : serverConfiguration) { + BootstrapConfig.ServerConfig serverConfig = setServerConfig((AbstractLwM2MBootstrapServerCredential) serverCredential); + configBs.servers.put(id, serverConfig); + BootstrapConfig.ServerSecurity serverSecurity = setServerSecurity((AbstractLwM2MBootstrapServerCredential) serverCredential, serverCredential.getSecurityMode()); + configBs.security.put(id, serverSecurity); + id++; + } + /** in LwM2mDefaultBootstrapSessionManager -> initTasks + * Delete all security/config objects if update bootstrap server and lwm2m server + * if other: del or update only instances */ - }); return configBs; } private BootstrapConfig.ServerSecurity setServerSecurity(AbstractLwM2MBootstrapServerCredential serverCredential, LwM2MSecurityMode securityMode) { BootstrapConfig.ServerSecurity serverSecurity = new BootstrapConfig.ServerSecurity(); String serverUri = "coap://"; - byte[] publicKeyOrId = new byte[]{};; - byte[] secretKey = new byte[]{};; + byte[] publicKeyOrId = new byte[]{}; + byte[] secretKey = new byte[]{}; + byte[] serverPublicKey = new byte[]{}; serverSecurity.serverId = serverCredential.getShortServerId(); + log.info("serverId = [{}]", serverSecurity.serverId); serverSecurity.securityMode = SecurityMode.valueOf(securityMode.name()); serverSecurity.bootstrapServer = serverCredential.isBootstrapServerIs(); if (!LwM2MSecurityMode.NO_SEC.equals(securityMode)) { - serverUri = "coaps://"; + AbstractLwM2MBootstrapClientCredentialWithKeys server; if (serverSecurity.bootstrapServer) { - publicKeyOrId = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.bootstrapServer).getDecodedClientPublicKeyOrId(); - secretKey = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.bootstrapServer).getDecodedClientSecretKey(); + server = (AbstractLwM2MBootstrapClientCredentialWithKeys) this.bootstrapServer; } else { - publicKeyOrId = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.lwm2mServer).getDecodedClientPublicKeyOrId(); - secretKey = ((AbstractLwM2MBootstrapClientCredentialWithKeys)this.lwm2mServer).getDecodedClientSecretKey(); + server = (AbstractLwM2MBootstrapClientCredentialWithKeys) this.lwm2mServer; } - + serverUri = "coaps://"; + if (LwM2MSecurityMode.PSK.equals(securityMode)) { + publicKeyOrId = server.getClientPublicKeyOrId().getBytes(); + secretKey = Hex.decodeHex(server.getClientSecretKey().toCharArray()); + log.info("publicKeyOrId [{}]", new String(publicKeyOrId, StandardCharsets.UTF_8)); + } else { + publicKeyOrId = server.getDecodedClientPublicKeyOrId(); + secretKey = server.getDecodedClientSecretKey(); + log.info("publicKeyOrId [{}]", Hex.encodeHexString(publicKeyOrId)); + } + serverPublicKey = serverCredential.getDecodedCServerPublicKey(); } + log.info("secretKey [{}]", Hex.encodeHexString(secretKey)); serverUri += (((serverCredential.getHost().equals("0.0.0.0") ? "localhost" : serverCredential.getHost()) + ":" + serverCredential.getPort())); log.info("serverSecurity.uri = [{}]", serverUri); - log.info("publicKeyOrId [{}]", Hex.encodeHexString(publicKeyOrId)); - log.info("secretKey [{}]", Hex.encodeHexString(secretKey)); - log.info("server [{}]", Hex.encodeHexString(serverCredential.getDecodedCServerPublicKey())); serverSecurity.uri = serverUri; serverSecurity.publicKeyOrId = publicKeyOrId; serverSecurity.secretKey = secretKey; - serverSecurity.serverPublicKey = serverCredential.getDecodedCServerPublicKey(); + serverSecurity.serverPublicKey = serverPublicKey; + log.info("server [{}]", Hex.encodeHexString(serverSecurity.serverPublicKey)); return serverSecurity; } + + private BootstrapConfig.ServerConfig setServerConfig (AbstractLwM2MBootstrapServerCredential serverCredential) { + BootstrapConfig.ServerConfig serverConfig = new BootstrapConfig.ServerConfig(); + serverConfig.shortId = serverCredential.getShortServerId(); + serverConfig.lifetime = serverCredential.getLifetime(); + serverConfig.defaultMinPeriod = serverCredential.getDefaultMinPeriod(); + serverConfig.notifIfDisabled = serverCredential.isNotifIfDisabled(); + serverConfig.binding = BindingMode.parse(serverCredential.getBinding()); + return serverConfig; + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java index 4f47079bbe..6f13b09f56 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java @@ -16,13 +16,14 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.request.BootstrapDiscoverRequest; import org.eclipse.leshan.core.request.BootstrapDownlinkRequest; import org.eclipse.leshan.core.request.BootstrapFinishRequest; import org.eclipse.leshan.core.request.BootstrapRequest; import org.eclipse.leshan.core.request.Identity; +import org.eclipse.leshan.core.response.BootstrapDiscoverResponse; import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore; -import org.eclipse.leshan.server.bootstrap.BootstrapConfigStoreTaskProvider; import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause; import org.eclipse.leshan.server.bootstrap.BootstrapSession; import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider; @@ -34,13 +35,13 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; import org.eclipse.leshan.server.security.SecurityChecker; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapConfigStoreTaskProvider; import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_INFO; @Slf4j @@ -59,7 +60,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession * @param bsSecurityStore the {@link BootstrapSecurityStore} used by default {@link SecurityChecker}. */ public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore, TransportService transportService) { - this(bsSecurityStore, new SecurityChecker(), new BootstrapConfigStoreTaskProvider(configStore), + this(bsSecurityStore, new SecurityChecker(), new LwM2MBootstrapConfigStoreTaskProvider(configStore), new StandardBootstrapModelProvider()); this.transportService = transportService; } @@ -164,14 +165,15 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession // store response DefaultBootstrapSession session = (DefaultBootstrapSession) bsSession; session.getResponses().add(response); - this.sendLogs (bsSession.getEndpoint(), - String.format("%s: %s %s receives success response %s for %s : %s", LOG_LWM2M_INFO, - request.getClass().getSimpleName(), request.getPath().toString(), response.toString(), bsSession.toString(), request.toString())); + String msg = String.format("%s: %s %s receives success response %s for %s : %s", LOG_LWM2M_INFO, + request.getClass().getSimpleName(), request.getPath().toString(), response.toString(), bsSession.toString(), request.toString()); + this.sendLogs(bsSession.getEndpoint(), msg); + // on success for NOT bootstrap finish request we send next request return BootstrapPolicy.continueWith(nextRequest(bsSession)); } else { // on success for bootstrap finish request we stop the session - this.sendLogs (bsSession.getEndpoint(), + this.sendLogs(bsSession.getEndpoint(), String.format("%s: %s receives success response for bootstrap finish request and stop the session: %s", LOG_LWM2M_INFO, request.getClass().getSimpleName(), bsSession.toString())); return BootstrapPolicy.finished(); @@ -194,9 +196,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession } else { // on response error for bootstrap finish request we stop the session this.sendLogs (bsSession.getEndpoint(), - String.format("%s: %s %s error response %s for request %s bootstrap finish. Stop the session: %s", LOG_LWM2M_INFO, - request.getClass().getSimpleName(), - request.getPath().toString(), response.toString(), request.toString(), bsSession.toString())); + String.format("%s: error response for request bootstrap finish. Stop the session: %s", LOG_LWM2M_ERROR, bsSession.toString())); return BootstrapPolicy.failed(); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java new file mode 100644 index 0000000000..ae95dd5ecc --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java @@ -0,0 +1,242 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.bootstrap.store; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.eclipse.leshan.core.request.*; +import org.eclipse.leshan.core.response.BootstrapDiscoverResponse; +import org.eclipse.leshan.core.response.BootstrapReadResponse; +import org.eclipse.leshan.core.response.LwM2mResponse; +import org.eclipse.leshan.server.bootstrap.BootstrapConfig; +import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore; +import org.eclipse.leshan.server.bootstrap.BootstrapSession; +import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider; +import org.eclipse.leshan.server.bootstrap.BootstrapUtil; + +import java.net.InetSocketAddress; +import java.util.*; + +import static org.eclipse.leshan.server.bootstrap.BootstrapUtil.toWriteRequest; + +@Slf4j +public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvider { + + private BootstrapConfigStore store; + + private Map supportedObjects; + + /** + * Map + */ + protected Map securityInstances; + protected Map serverInstances; + + public LwM2MBootstrapConfigStoreTaskProvider(BootstrapConfigStore store) { + this.store = store; + } + + @Override + public Tasks getTasks(BootstrapSession session, List previousResponse) { + + BootstrapConfig config = store.get(session.getEndpoint(), session.getIdentity(), session); + if (config == null) { + return null; + } + if (previousResponse == null && shouldStartWithDiscover(config)) { + Tasks tasks = new Tasks(); + tasks.requestsToSend = new ArrayList<>(1); + tasks.requestsToSend.add(new BootstrapDiscoverRequest()); + tasks.last = false; + return tasks; + } else { + Tasks tasks = new Tasks(); + if (this.supportedObjects == null) { + initSupportedObjectsDefault(); + + } + // add supportedObjects + tasks.supportedObjects = this.supportedObjects; + // handle bootstrap discover response + if (previousResponse != null) { + if (previousResponse.get(0) instanceof BootstrapDiscoverResponse) { + BootstrapDiscoverResponse discoverResponse = (BootstrapDiscoverResponse) previousResponse.get(0); + if (discoverResponse.isSuccess()) { + this.initAfterBootstrapDiscover(discoverResponse); + findSecurityInstanceId(discoverResponse.getObjectLinks()); + } else { + log.info( + "Bootstrap Discover return error {} : to continue bootstrap session without autoIdForSecurityObject mode. {}", + discoverResponse, session); + } + if (this.securityInstances.get(0) == null) { + log.error( + "Unable to find bootstrap server instance in Security Object (0) in response {}: unable to continue bootstrap session with autoIdForSecurityObject mode. {}", + discoverResponse, session); + return null; + } + tasks.requestsToSend = new ArrayList<>(1); + tasks.requestsToSend.add(new BootstrapReadRequest("/1")); + tasks.last = false; + return tasks; + } + BootstrapReadResponse readResponse = (BootstrapReadResponse) previousResponse.get(0); + findServerInstanceId(readResponse); + // create requests from config + tasks.requestsToSend = this.toRequests(config, + config.contentFormat != null ? config.contentFormat : session.getContentFormat(), + this.securityInstances.get(0)); + } else { + // create requests from config + tasks.requestsToSend = BootstrapUtil.toRequests(config, + config.contentFormat != null ? config.contentFormat : session.getContentFormat()); + + } + return tasks; + } + } + + protected boolean shouldStartWithDiscover(BootstrapConfig config) { + return config.autoIdForSecurityObject; + } + + protected void findSecurityInstanceId(Link[] objectLinks) { + this.securityInstances = new HashMap<>(); + for (Link link : objectLinks) { + if (link.getUrl().startsWith("/0/")) { + try { + LwM2mPath path = new LwM2mPath(link.getUrl()); + if (path.isObjectInstance()) { + if (link.getAttributes().containsKey("ssid")) { + int serverId = Integer.valueOf(link.getAttributes().get("ssid")); + if (!this.securityInstances.containsKey(serverId)) { + this.securityInstances.put(serverId, path.getObjectInstanceId()); + } else { + log.error(String.format("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId())); + } + this.securityInstances.put(Integer.valueOf(link.getAttributes().get("ssid")), path.getObjectInstanceId()); + } else { + if (!this.securityInstances.containsKey(0)) { + this.securityInstances.put(0, path.getObjectInstanceId()); + } else { + log.error(String.format("Invalid bootstrapSecurityInstance by [{}]", path.getObjectInstanceId())); + } + } + } + } catch (Exception e) { + // ignore if this is not a LWM2M path + log.error("Invalid LwM2MPath starting by \"/0/\""); + } + } + } + } + + protected void findServerInstanceId(BootstrapReadResponse readResponse) { + serverInstances = new HashMap<>(); + ((LwM2mObject) readResponse.getContent()).getInstances().values().forEach(instance -> { + serverInstances.put(((Long) instance.getResource(0).getValue()).intValue(), instance.getId()); + }); + } + + public BootstrapConfigStore getStore() { + return this.store; + } + + private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) { + Link[] links = response.getObjectLinks(); + Arrays.stream(links).forEach(link -> { + LwM2mPath path = new LwM2mPath(link.getUrl()); + if (path != null && !path.isRoot() && path.getObjectId() < 3) { + if (path.isObject()) { + String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver") : "1.0"; + this.supportedObjects.put(path.getObjectId(), ver); + } + } + }); + } + + + public List> toRequests(BootstrapConfig bootstrapConfig, + ContentFormat contentFormat, int bootstrapSecurityInstanceId) { + List> requests = new ArrayList<>(); + boolean isBsServer = false; + boolean isLwServer = false; + /** Map */ + Map instances = new HashMap<>(); + // handle security + int id = 0; + for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) { + if (security.bootstrapServer) { + requests.add(toWriteRequest(bootstrapSecurityInstanceId, security, contentFormat)); + isBsServer = true; + instances.put(security.serverId, bootstrapSecurityInstanceId); + } else { + if (id == bootstrapSecurityInstanceId) { + id++; + } + requests.add(toWriteRequest(id, security, contentFormat)); + instances.put(security.serverId, id); + id++; + } + } + // handle server + for (Map.Entry server : bootstrapConfig.servers.entrySet()) { + int securityInstanceId = instances.get(server.getValue().shortId); + requests.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat)); + if (!isBsServer && this.serverInstances.containsKey(server.getValue().shortId) && securityInstanceId != this.serverInstances.get(server.getValue().shortId)){ + requests.add(new BootstrapDeleteRequest("/0/" + securityInstanceId)); + requests.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(server.getValue().shortId))); + } + isLwServer = true; + } + // handle acl + for (Map.Entry acl : bootstrapConfig.acls.entrySet()) { + requests.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); + } + // handle delete + if (isBsServer & isLwServer) { + requests.add(new BootstrapDeleteRequest("/0")); + requests.add(new BootstrapDeleteRequest("/1")); + } + + return (requests); + } + + private InetSocketAddress getSocketAddress(String uri) { +// String uri1 = "coap://localhost:5687"; + if (uri.contains("coap://")) { + uri = uri.replace("coap://", ""); + } else return null; + String[] uris = uri.split(":"); + if (uris.length == 2) { + try { + return new InetSocketAddress(uris[0], Integer.parseInt(uris[1])); + } catch (Exception e) { + return null; + } + + } else return null; + } + + private void initSupportedObjectsDefault() { + this.supportedObjects = new HashMap<>(); + this.supportedObjects.put(0, "1.1"); + this.supportedObjects.put(1, "1.1"); + this.supportedObjects.put(2, "1.0"); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java index ee973f3a23..81d2007c4b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java @@ -75,6 +75,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { if (store.getBootstrapCredentialConfig() != null) { /* add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); + endPoint = store.getEndpoint(); BootstrapConfig bsConfigNew = store.getBootstrapConfig(); if (bsConfigNew != null) { try { @@ -121,34 +122,6 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { /* BootstrapConfig */ LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store); if (lwM2MBootstrapConfig != null) { - /* Security info */ -// switch (lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode()) { -// /* Use RPK only */ -// case PSK: -// store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndpoint(), -// lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId(), -// Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientSecretKey().toCharArray()))); -// store.setSecurityMode(SecurityMode.PSK); -// break; -// case RPK: -// try { -//// store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndpoint(), -//// SecurityUtil.publicKey.decode(Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId().toCharArray())))); -//// store.setSecurityMode(SecurityMode.RPK); -// break; -// } catch (IOException | GeneralSecurityException e) { -// log.error("Unable to decode Client public key for [{}] [{}]", store.getEndpoint(), e.getMessage()); -// } -// case X509: -// store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndpoint())); -// store.setSecurityMode(SecurityMode.X509); -// break; -// case NO_SEC: -// store.setSecurityMode(SecurityMode.NO_SEC); -// store.setSecurityInfo(null); -// break; -// default: -// } BootstrapConfig bootstrapConfig = lwM2MBootstrapConfig.getLwM2MBootstrapConfig(); store.setBootstrapConfig(bootstrapConfig); } @@ -157,27 +130,11 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) { LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); if (lwM2MBootstrapConfig != null) { -// LwM2MBootstrapServersConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); -// lwM2MBootstrapConfig.setServers(JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class)); -// LwM2MServerBootstrap bootstrapServerProfile = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class); -// if (SecurityMode.NO_SEC != bootstrapServerProfile.getSecurityMode() && bootstrapServerProfile != null) { -// bootstrapServerProfile.setSecurityHost(bootstrapServerProfile.getHost()); -// bootstrapServerProfile.setSecurityPort(bootstrapServerProfile.getPort()); -// } -// LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); -// if (SecurityMode.NO_SEC != profileLwm2mServer.getSecurityMode() && profileLwm2mServer != null) { -// profileLwm2mServer.setSecurityHost(profileLwm2mServer.getHost()); -// profileLwm2mServer.setSecurityPort(profileLwm2mServer.getPort()); -// } - - UUID sessionUUiD = UUID.randomUUID(); TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); bsSessions.put(store.getEndpoint(), sessionInfo); context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService())); if (this.getValidatedSecurityMode(lwM2MBootstrapConfig)) { -// lwM2MBootstrapConfig.setBootstrapServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile)); -// lwM2MBootstrapConfig.setLwm2mServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer)); String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint()); helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); return lwM2MBootstrapConfig; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index c8431da85c..ec8afa77f0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -48,6 +48,9 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.service.DataValidator; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -238,7 +241,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen case PSK: PSKClientCredential pskCredentials = (PSKClientCredential) clientCredentials; if (StringUtils.isBlank(pskCredentials.getIdentity())) { - throw new DeviceCredentialsValidationException("LwM2M client PSK identity must be specified!"); + throw new DeviceCredentialsValidationException("LwM2M client PSK identity must be specified and must be an utf8 string!"); } // SecurityMode.NO_SEC.toString() == "NO_SEC"; if (pskCredentials.getIdentity().equals(SecurityMode.NO_SEC.toString())) { @@ -295,7 +298,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen case PSK: PSKBootstrapClientCredential pskCredentials = (PSKBootstrapClientCredential) serverCredentials; if (StringUtils.isBlank(pskCredentials.getClientPublicKeyOrId())) { - throw new DeviceCredentialsValidationException(server + " client PSK public key or id must be specified!"); + throw new DeviceCredentialsValidationException(server + " client PSK public key or id must be specified and must be an utf8 string!"); } // SecurityMode.NO_SEC.toString() == "NO_SEC"; From 22609c34934ec2800db5646b1810e54e1039e55d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 16 Nov 2021 12:19:05 +0200 Subject: [PATCH 6/6] lwm2m - Bootstrap add delete --- ...LwM2MBootstrapConfigStoreTaskProvider.java | 75 +++++++++++-------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java index ae95dd5ecc..bb84f94c59 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java @@ -31,6 +31,7 @@ import org.eclipse.leshan.server.bootstrap.BootstrapUtil; import java.net.InetSocketAddress; import java.util.*; +import java.util.stream.Collectors; import static org.eclipse.leshan.server.bootstrap.BootstrapUtil.toWriteRequest; @@ -46,6 +47,8 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi */ protected Map securityInstances; protected Map serverInstances; + protected Integer bootstrapServerIdOld; + protected Integer bootstrapServerIdNew; public LwM2MBootstrapConfigStoreTaskProvider(BootstrapConfigStore store) { this.store = store; @@ -99,8 +102,7 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi findServerInstanceId(readResponse); // create requests from config tasks.requestsToSend = this.toRequests(config, - config.contentFormat != null ? config.contentFormat : session.getContentFormat(), - this.securityInstances.get(0)); + config.contentFormat != null ? config.contentFormat : session.getContentFormat()); } else { // create requests from config tasks.requestsToSend = BootstrapUtil.toRequests(config, @@ -116,6 +118,7 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi } protected void findSecurityInstanceId(Link[] objectLinks) { + log.info("Object after discover: [{}]", objectLinks); this.securityInstances = new HashMap<>(); for (Link link : objectLinks) { if (link.getUrl().startsWith("/0/")) { @@ -147,10 +150,20 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi } protected void findServerInstanceId(BootstrapReadResponse readResponse) { - serverInstances = new HashMap<>(); + this.serverInstances = new HashMap<>(); ((LwM2mObject) readResponse.getContent()).getInstances().values().forEach(instance -> { serverInstances.put(((Long) instance.getResource(0).getValue()).intValue(), instance.getId()); }); + if (this.securityInstances != null && this.securityInstances.size() > 0 && this.serverInstances != null && this.serverInstances.size() > 0) { + this.findBootstrapServerId(); + } + } + + protected void findBootstrapServerId() { + Map filteredMap = this.serverInstances.entrySet() + .stream().filter(x -> !this.securityInstances.containsKey(x.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + this.bootstrapServerIdOld = filteredMap.keySet().stream().findFirst().get(); } public BootstrapConfigStore getStore() { @@ -172,8 +185,10 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi public List> toRequests(BootstrapConfig bootstrapConfig, - ContentFormat contentFormat, int bootstrapSecurityInstanceId) { + ContentFormat contentFormat) { List> requests = new ArrayList<>(); + List> requestsDelete = new ArrayList<>(); + List> requestsWrite = new ArrayList<>(); boolean isBsServer = false; boolean isLwServer = false; /** Map */ @@ -182,57 +197,55 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi int id = 0; for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) { if (security.bootstrapServer) { - requests.add(toWriteRequest(bootstrapSecurityInstanceId, security, contentFormat)); + requestsWrite.add(toWriteRequest(this.securityInstances.get(0), security, contentFormat)); isBsServer = true; - instances.put(security.serverId, bootstrapSecurityInstanceId); + this.bootstrapServerIdNew = security.serverId; + instances.put(security.serverId, this.securityInstances.get(0)); } else { - if (id == bootstrapSecurityInstanceId) { + if (id == this.securityInstances.get(0)) { id++; } - requests.add(toWriteRequest(id, security, contentFormat)); + requestsWrite.add(toWriteRequest(id, security, contentFormat)); instances.put(security.serverId, id); + isLwServer = true; + if (!isBsServer && this.securityInstances.containsKey(security.serverId) && id != this.securityInstances.get(security.serverId)) { + requestsDelete.add(new BootstrapDeleteRequest("/0/" + this.securityInstances.get(security.serverId))); + } id++; } } // handle server for (Map.Entry server : bootstrapConfig.servers.entrySet()) { int securityInstanceId = instances.get(server.getValue().shortId); - requests.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat)); - if (!isBsServer && this.serverInstances.containsKey(server.getValue().shortId) && securityInstanceId != this.serverInstances.get(server.getValue().shortId)){ - requests.add(new BootstrapDeleteRequest("/0/" + securityInstanceId)); - requests.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(server.getValue().shortId))); + requestsWrite.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat)); + if (!isLwServer && this.bootstrapServerIdNew != null && server.getValue().shortId == this.bootstrapServerIdNew && + (this.bootstrapServerIdNew != this.bootstrapServerIdOld || securityInstanceId != this.serverInstances.get(this.bootstrapServerIdOld))) { + requestsDelete.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(this.bootstrapServerIdOld))); + + } else { + if (!isBsServer && this.serverInstances.containsKey(server.getValue().shortId) && securityInstanceId != this.serverInstances.get(server.getValue().shortId)) { + requestsDelete.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(server.getValue().shortId))); + } } - isLwServer = true; } // handle acl for (Map.Entry acl : bootstrapConfig.acls.entrySet()) { - requests.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); + requestsWrite.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); } // handle delete if (isBsServer & isLwServer) { requests.add(new BootstrapDeleteRequest("/0")); requests.add(new BootstrapDeleteRequest("/1")); + } else if (requestsDelete.size() > 0) { + requests.addAll(requestsDelete); + } + // handle write + if (requestsWrite.size() > 0) { + requests.addAll(requestsWrite); } - return (requests); } - private InetSocketAddress getSocketAddress(String uri) { -// String uri1 = "coap://localhost:5687"; - if (uri.contains("coap://")) { - uri = uri.replace("coap://", ""); - } else return null; - String[] uris = uri.split(":"); - if (uris.length == 2) { - try { - return new InetSocketAddress(uris[0], Integer.parseInt(uris[1])); - } catch (Exception e) { - return null; - } - - } else return null; - } - private void initSupportedObjectsDefault() { this.supportedObjects = new HashMap<>(); this.supportedObjects.put(0, "1.1");