diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 936f30af60..395eefeb45 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1044,6 +1044,8 @@ transport: dtls: # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${LWM2M_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" + # "" disables connection id support, 0 enables support but not for incoming traffic, any value greater than 0 set the connection id size in bytes + connection_id_length: "${LWM2M_DTLS_CONNECTION_ID_LENGTH:6}" server: # LwM2M Server ID id: "${LWM2M_SERVER_ID:123}" 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 4bdcbcf645..a9166b0bf1 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 @@ -38,6 +38,7 @@ import javax.annotation.PreDestroy; import java.security.cert.X509Certificate; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CURVES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; @@ -95,6 +96,7 @@ public class LwM2MTransportBootstrapService { dtlsConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, serverConfig.isRecommendedSupportedGroups()); dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, serverConfig.isRecommendedCiphers()); dtlsConfig.set(DTLS_RETRANSMISSION_TIMEOUT, serverConfig.getDtlsRetransmissionTimeout(), MILLISECONDS); + dtlsConfig.set(DTLS_CONNECTION_ID_LENGTH, serverConfig.getDtlsConnectionIdLength()); dtlsConfig.set(DTLS_ROLE, SERVER_ONLY); setServerWithCredentials(builder, dtlsConfig); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index 0ccf6e1969..dc2e9f6d45 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -41,6 +41,10 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { @Value("${transport.lwm2m.dtls.retransmission_timeout:9000}") private int dtlsRetransmissionTimeout; + @Getter + @Value("${transport.lwm2m.dtls.connection_id_length:6}") + private Integer dtlsConnectionIdLength; + @Getter @Value("${transport.lwm2m.timeout:}") private Long timeout; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 4037251572..7d54e97cde 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -43,6 +43,7 @@ import javax.annotation.PreDestroy; import java.security.cert.X509Certificate; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CURVES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; @@ -139,6 +140,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { dtlsConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, config.isRecommendedSupportedGroups()); dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, config.isRecommendedCiphers()); dtlsConfig.set(DTLS_RETRANSMISSION_TIMEOUT, config.getDtlsRetransmissionTimeout(), MILLISECONDS); + dtlsConfig.set(DTLS_CONNECTION_ID_LENGTH, config.getDtlsConnectionIdLength()); dtlsConfig.set(DTLS_ROLE, SERVER_ONLY); /* Create credentials */ diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServiceTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServiceTest.java new file mode 100644 index 0000000000..23293c7b7a --- /dev/null +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServiceTest.java @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2024 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; + +import org.eclipse.californium.core.network.CoapEndpoint; +import org.eclipse.californium.scandium.config.DtlsConnectorConfig; +import org.eclipse.leshan.server.californium.LeshanServer; +import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; +import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.TbLwM2MDtlsBootstrapCertificateVerifier; +import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; +import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; +import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.when; + +@ExtendWith(MockitoExtension.class) +public class LwM2MTransportBootstrapServiceTest { + + @Mock + private LwM2MTransportServerConfig serverConfig; + @Mock + private LwM2MTransportBootstrapConfig bootstrapConfig; + @Mock + private LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore; + @Mock + private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore; + @Mock + private TransportService transportService; + @Mock + private TbLwM2MDtlsBootstrapCertificateVerifier certificateVerifier; + + + @Test + public void getLHServer_creates_ConnectionIdGenerator_when_connection_id_length_not_null(){ + final Integer CONNECTION_ID_LENGTH = 6; + when(serverConfig.getDtlsConnectionIdLength()).thenReturn(CONNECTION_ID_LENGTH); + var lwM2MBootstrapService = createLwM2MBootstrapService(); + + var server = lwM2MBootstrapService.getLhBootstrapServer(); + var securedEndpoint = (CoapEndpoint) ReflectionTestUtils.getField(server, "securedEndpoint"); + assertThat(securedEndpoint).isNotNull(); + + var config = (DtlsConnectorConfig) ReflectionTestUtils.getField(securedEndpoint.getConnector(), "config"); + assertThat(config).isNotNull(); + assertThat(config.getConnectionIdGenerator()).isNotNull(); + assertThat((Integer) ReflectionTestUtils.getField(config.getConnectionIdGenerator(), "connectionIdLength")) + .isEqualTo(CONNECTION_ID_LENGTH); + } + + @Test + public void getLHServer_creates_no_ConnectionIdGenerator_when_connection_id_length_is_null(){ + when(serverConfig.getDtlsConnectionIdLength()).thenReturn(null); + var lwM2MBootstrapService = createLwM2MBootstrapService(); + + var server = lwM2MBootstrapService.getLhBootstrapServer(); + var securedEndpoint = (CoapEndpoint) ReflectionTestUtils.getField(server, "securedEndpoint"); + assertThat(securedEndpoint).isNotNull(); + + var config = (DtlsConnectorConfig) ReflectionTestUtils.getField(securedEndpoint.getConnector(), "config"); + assertThat(config).isNotNull(); + assertThat(config.getConnectionIdGenerator()).isNull(); + } + + private LwM2MTransportBootstrapService createLwM2MBootstrapService() { + setDefaultConfigVariables(); + return new LwM2MTransportBootstrapService(serverConfig, bootstrapConfig, lwM2MBootstrapSecurityStore, + lwM2MInMemoryBootstrapConfigStore, transportService, certificateVerifier); + } + + private void setDefaultConfigVariables(){ + when(bootstrapConfig.getPort()).thenReturn(5683); + when(bootstrapConfig.getSecurePort()).thenReturn(5684); + when(serverConfig.isRecommendedCiphers()).thenReturn(false); + when(serverConfig.getDtlsRetransmissionTimeout()).thenReturn(9000); + } + + +} \ No newline at end of file diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfigTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfigTest.java new file mode 100644 index 0000000000..7e657dd932 --- /dev/null +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfigTest.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2024 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.config; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootContextLoader; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@EnableConfigurationProperties(value = LwM2MTransportServerConfig.class) +@ContextConfiguration(classes = {LwM2MTransportServerConfig.class}, loader = SpringBootContextLoader.class) +@TestPropertySource(properties = { + "transport.sessions.report_timeout=10", + "transport.lwm2m.security.recommended_ciphers=true", + "transport.lwm2m.security.recommended_supported_groups=true", + "transport.lwm2m.downlink_pool_size=10", + "transport.lwm2m.uplink_pool_size=10", + "transport.lwm2m.ota_pool_size=10", + "transport.lwm2m.clean_period_in_sec=2", + "transport.lwm2m.dtls.connection_id_length=" + +}) +class LwM2MTransportServerConfigTest { + + @MockBean(name = "lwm2mServerCredentials") + private SslCredentialsConfig credentialsConfig; + + @MockBean(name = "lwm2mTrustCredentials") + private SslCredentialsConfig trustCredentialsConfig; + + @Autowired + private LwM2MTransportServerConfig serverConfig; + + @Test + void getDtlsConnectionIdLength_return_null_is_property_is_empty() { + // note: transport.lwm2m.dtls.connect_id_length is set in TestPropertySource + assertThat(serverConfig.getDtlsConnectionIdLength()).isNull(); + } +} \ No newline at end of file diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportServiceTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportServiceTest.java new file mode 100644 index 0000000000..92f60ebb8e --- /dev/null +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportServiceTest.java @@ -0,0 +1,109 @@ +/** + * Copyright © 2016-2024 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.server; + +import org.eclipse.californium.core.network.CoapEndpoint; +import org.eclipse.californium.scandium.config.DtlsConnectorConfig; +import org.eclipse.leshan.server.californium.LeshanServer; +import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; +import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.when; + +@ExtendWith(MockitoExtension.class) +public class DefaultLwM2mTransportServiceTest { + + @Mock + private LwM2mTransportContext context; + + @Mock + private LwM2MTransportServerConfig config; + @Mock + private OtaPackageDataCache otaPackageDataCache; + @Mock + private LwM2mUplinkMsgHandler handler; + @Mock + private CaliforniumRegistrationStore registrationStore; + @Mock + private TbSecurityStore securityStore; + @Mock + private TbLwM2MDtlsCertificateVerifier certificateVerifier; + @Mock + private TbLwM2MAuthorizer authorizer; + @Mock + private LwM2mVersionedModelProvider modelProvider; + + + @Test + public void getLHServer_creates_ConnectionIdGenerator_when_connection_id_length_not_null(){ + final Integer CONNECTION_ID_LENGTH = 6; + when(config.getDtlsConnectionIdLength()).thenReturn(CONNECTION_ID_LENGTH); + var lwm2mService = createLwM2MService(); + + LeshanServer server = ReflectionTestUtils.invokeMethod(lwm2mService, "getLhServer"); + + assertThat(server).isNotNull(); + var securedEndpoint = (CoapEndpoint) ReflectionTestUtils.getField(server, "securedEndpoint"); + assertThat(securedEndpoint).isNotNull(); + + var config = (DtlsConnectorConfig) ReflectionTestUtils.getField(securedEndpoint.getConnector(), "config"); + assertThat(config).isNotNull(); + assertThat(config.getConnectionIdGenerator()).isNotNull(); + assertThat((Integer) ReflectionTestUtils.getField(config.getConnectionIdGenerator(), "connectionIdLength")) + .isEqualTo(CONNECTION_ID_LENGTH); + } + + @Test + public void getLHServer_creates_no_ConnectionIdGenerator_when_connection_id_length_is_null(){ + when(config.getDtlsConnectionIdLength()).thenReturn(null); + var lwm2mService = createLwM2MService(); + + LeshanServer server = ReflectionTestUtils.invokeMethod(lwm2mService, "getLhServer"); + + assertThat(server).isNotNull(); + var securedEndpoint = (CoapEndpoint) ReflectionTestUtils.getField(server, "securedEndpoint"); + assertThat(securedEndpoint).isNotNull(); + var config = (DtlsConnectorConfig) ReflectionTestUtils.getField(securedEndpoint.getConnector(), "config"); + assertThat(config).isNotNull(); + assertThat(config.getConnectionIdGenerator()).isNull(); + } + + private DefaultLwM2mTransportService createLwM2MService() { + setDefaultConfigVariables(); + return new DefaultLwM2mTransportService(context, config, otaPackageDataCache, handler, registrationStore, + securityStore, certificateVerifier, authorizer, modelProvider); + } + + private void setDefaultConfigVariables(){ + when(config.getPort()).thenReturn(5683); + when(config.getSecurePort()).thenReturn(5684); + when(config.isRecommendedCiphers()).thenReturn(false); + when(config.getDtlsRetransmissionTimeout()).thenReturn(9000); + } + + +} \ No newline at end of file diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 688392d264..b6dc7248ab 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -155,6 +155,8 @@ transport: dtls: # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${LWM2M_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" + # "" disables connection id support, 0 enables support but not for incoming traffic, any value greater than 0 set the connection id size in bytes + connection_id_length: "${LWM2M_DTLS_CONNECTION_ID_LENGTH:6}" server: # LwM2M Server ID id: "${LWM2M_SERVER_ID:123}"