Browse Source

lwm2ml: ver 3.9.1 - start

pull/9903/head
nick 3 years ago
parent
commit
74daec474f
  1. 51
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  2. 147
      application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/DefaultLwM2mAttributeParserTest.java
  3. 76
      application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/DefaultLwM2mLinkParserTest.java
  4. 87
      application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/LwM2mAttributesTest.java
  5. 20
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  6. 243
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  7. 5
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java
  8. 3
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java
  9. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
  10. 11
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/Lwm2mServer.java
  11. 17
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java
  12. 19
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java
  13. 12
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java
  14. 7
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java
  15. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java
  16. 17
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java
  17. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java
  18. 36
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java
  19. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java
  20. 67
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java
  21. 35
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  22. 18
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java
  23. 9
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
  24. 10
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java
  25. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java
  26. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java
  27. 2
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java
  28. 6
      common/data/src/main/java/org/thingsboard/server/common/data/util/TbDDFFileParser.java
  29. 129
      common/data/src/main/java/org/thingsboard/server/common/data/util/TbDefaultDDFFileValidator.java
  30. 149
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  31. 35
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java
  32. 13
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java
  33. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java
  34. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MConfigurationChecker.java
  35. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java
  36. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  37. 38
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MAuthorizer.java
  38. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java
  39. 141
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  40. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MNetworkConfig.java
  41. 12
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  42. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java
  43. 10
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java
  44. 21
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  45. 88
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java
  46. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java
  47. 115
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java
  48. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java
  49. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java
  50. 99
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java
  51. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java
  52. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java
  53. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java
  54. 383
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java
  55. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java
  56. 133
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java
  57. 9
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  58. 10
      pom.xml
  59. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts

51
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.leshan.client.californium.LeshanClient; import org.eclipse.leshan.client.LeshanClient;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.junit.After; import org.junit.After;
@ -88,7 +88,6 @@ import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.core.config.CoapConfig.COAP_PORT; import static org.eclipse.californium.core.config.CoapConfig.COAP_PORT;
import static org.eclipse.californium.core.config.CoapConfig.COAP_SECURE_PORT; import static org.eclipse.californium.core.config.CoapConfig.COAP_SECURE_PORT;
import static org.eclipse.leshan.client.object.Security.noSec; import static org.eclipse.leshan.client.object.Security.noSec;
import static org.eclipse.leshan.client.object.Security.noSecBootstap;
import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED;
@ -121,8 +120,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
public static final String host = "localhost"; public static final String host = "localhost";
public static final String hostBs = "localhost"; public static final String hostBs = "localhost";
public static final int shortServerId = 123; public static final Integer shortServerId = 123;
public static final int shortServerIdBs = 111; public static final Integer shortServerIdBs0 = 0;
public static final int serverId = 1;
public static final int serverIdBs = 0;
public static final String COAP = "coap://"; public static final String COAP = "coap://";
public static final String COAPS = "coaps://"; public static final String COAPS = "coaps://";
public static final String URI = COAP + host + ":" + port; public static final String URI = COAP + host + ":" + port;
@ -132,7 +134,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
public static final Configuration COAP_CONFIG = new Configuration().set(COAP_PORT, port).set(COAP_SECURE_PORT, securityPort); public static final Configuration COAP_CONFIG = new Configuration().set(COAP_PORT, port).set(COAP_SECURE_PORT, securityPort);
public static Configuration COAP_CONFIG_BS = new Configuration().set(COAP_PORT, portBs).set(COAP_SECURE_PORT, securityPortBs); public static Configuration COAP_CONFIG_BS = new Configuration().set(COAP_PORT, portBs).set(COAP_SECURE_PORT, securityPortBs);
public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId); public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId);
public static final Security SECURITY_NO_SEC_BS = noSecBootstap(URI_BS);
protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS = protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
" {\n" + " {\n" +
@ -235,8 +236,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
getWsClient().waitForReply(); getWsClient().waitForReply();
getWsClient().registerWaitForUpdate(); getWsClient().registerWaitForUpdate();
createNewClient(security, coapConfig, false, endpoint, false, null); createNewClient(security, null, coapConfig, false, endpoint);
awaitObserveReadAll(0, false, device.getId().getId().toString()); awaitObserveReadAll(0, device.getId().getId().toString());
String msg = getWsClient().waitForUpdate(); String msg = getWsClient().waitForUpdate();
EntityDataUpdate update = JacksonUtil.fromString(msg, EntityDataUpdate.class); EntityDataUpdate update = JacksonUtil.fromString(msg, EntityDataUpdate.class);
@ -252,6 +253,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
int expectedMin = 5; int expectedMin = 5;
Assert.assertTrue(expectedMax >= Long.parseLong(tsValue.getValue())); Assert.assertTrue(expectedMax >= Long.parseLong(tsValue.getValue()));
Assert.assertTrue(expectedMin <= Long.parseLong(tsValue.getValue())); Assert.assertTrue(expectedMin <= Long.parseLong(tsValue.getValue()));
} }
protected void createDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) throws Exception { protected void createDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) throws Exception {
@ -300,14 +303,14 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
this.resources = resources; this.resources = resources;
} }
public void createNewClient(Security security, Configuration coapConfig, boolean isRpc, String endpoint, boolean isBootstrap, Security securityBs) throws Exception { public void createNewClient(Security security, Security securityBs, Configuration coapConfig, boolean isRpc, String endpoint) throws Exception {
this.clientDestroy(); this.clientDestroy();
lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint); lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint);
try (ServerSocket socket = new ServerSocket(0)) { try (ServerSocket socket = new ServerSocket(0)) {
int clientPort = socket.getLocalPort(); int clientPort = socket.getLocalPort();
lwM2MTestClient.init(security, coapConfig, clientPort, isRpc, isBootstrap, this.shortServerId, this.shortServerIdBs, lwM2MTestClient.init(security, securityBs, coapConfig, clientPort, isRpc,
securityBs, this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest); this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest);
} }
} }
@ -354,7 +357,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
private AbstractLwM2MBootstrapServerCredential getBootstrapServerCredentialNoSec(boolean isBootstrap) { private AbstractLwM2MBootstrapServerCredential getBootstrapServerCredentialNoSec(boolean isBootstrap) {
AbstractLwM2MBootstrapServerCredential bootstrapServerCredential = new NoSecLwM2MBootstrapServerCredential(); AbstractLwM2MBootstrapServerCredential bootstrapServerCredential = new NoSecLwM2MBootstrapServerCredential();
bootstrapServerCredential.setServerPublicKey(""); bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs : shortServerId); bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
bootstrapServerCredential.setPort(isBootstrap ? portBs : port); bootstrapServerCredential.setPort(isBootstrap ? portBs : port);
@ -397,20 +400,18 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
.until(() -> leshanClient.getRegisteredServers().size() == 0); .until(() -> leshanClient.getRegisteredServers().size() == 0);
} }
protected void awaitObserveReadAll(int cntObserve, boolean isBootstrap, String deviceIdStr) throws Exception { protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception {
if (!isBootstrap) { await("ObserveReadAll after start client: countObserve " + cntObserve)
await("ObserveReadAll after start client: countObserve " + cntObserve) .atMost(40, TimeUnit.SECONDS)
.atMost(40, TimeUnit.SECONDS) .until(() -> {
.until(() -> { String actualResultReadAll = sendObserve("ObserveReadAll", null, deviceIdStr);
String actualResultReadAll = sendObserve("ObserveReadAll", null, deviceIdStr); ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class);
ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class); Assert.assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText());
Assert.assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText()); String actualValuesReadAll = rpcActualResultReadAll.get("value").asText();
String actualValuesReadAll = rpcActualResultReadAll.get("value").asText(); log.warn("ObserveReadAll: [{}]", actualValuesReadAll);
log.warn("ObserveReadAll: [{}]", actualValuesReadAll); int actualCntObserve = "[]".equals(actualValuesReadAll) ? 0 : actualValuesReadAll.split(",").length;
int actualCntObserve = "[]".equals(actualValuesReadAll) ? 0 : actualValuesReadAll.split(",").length; return cntObserve == actualCntObserve;
return cntObserve == actualCntObserve; });
});
}
} }
protected String sendObserve(String method, String params, String deviceIdStr) throws Exception { protected String sendObserve(String method, String params, String deviceIdStr) throws Exception {

147
application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/DefaultLwM2mAttributeParserTest.java

@ -0,0 +1,147 @@
/**
* Copyright © 2016-2023 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.attributes;
import org.eclipse.leshan.core.link.LinkParseException;
import org.eclipse.leshan.core.link.attributes.InvalidAttributeException;
import org.eclipse.leshan.core.link.lwm2m.attributes.DefaultLwM2mAttributeParser;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttribute;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class DefaultLwM2mAttributeParserTest {
private final DefaultLwM2mAttributeParser parser = new DefaultLwM2mAttributeParser();
private static Stream<Arguments> validAttributes() throws InvalidAttributeException {
return Stream.of(//
Arguments.of("pmin", LwM2mAttributes.create(LwM2mAttributes.MINIMUM_PERIOD)), //
Arguments.of("pmin=30", LwM2mAttributes.create(LwM2mAttributes.MINIMUM_PERIOD, 30l)), //
Arguments.of("pmax", LwM2mAttributes.create(LwM2mAttributes.MAXIMUM_PERIOD)), //
Arguments.of("pmax=60", LwM2mAttributes.create(LwM2mAttributes.MAXIMUM_PERIOD, 60l)), //
Arguments.of("dim=2", LwM2mAttributes.create(LwM2mAttributes.DIMENSION, 2l)), //
Arguments.of("epmin", LwM2mAttributes.create(LwM2mAttributes.EVALUATE_MINIMUM_PERIOD)), //
Arguments.of("epmin=30", LwM2mAttributes.create(LwM2mAttributes.EVALUATE_MINIMUM_PERIOD, 30l)), //
Arguments.of("epmax", LwM2mAttributes.create(LwM2mAttributes.EVALUATE_MAXIMUM_PERIOD)), //
Arguments.of("epmax=60", LwM2mAttributes.create(LwM2mAttributes.EVALUATE_MAXIMUM_PERIOD, 60l)), //
Arguments.of("lt", LwM2mAttributes.create(LwM2mAttributes.LESSER_THAN)), //
Arguments.of("lt=30", LwM2mAttributes.create(LwM2mAttributes.LESSER_THAN, 30d)), //
Arguments.of("lt=-30", LwM2mAttributes.create(LwM2mAttributes.LESSER_THAN, -30d)), //
Arguments.of("lt=30.55", LwM2mAttributes.create(LwM2mAttributes.LESSER_THAN, 30.55)), //
Arguments.of("lt=-30.55", LwM2mAttributes.create(LwM2mAttributes.LESSER_THAN, -30.55)), //
Arguments.of("gt", LwM2mAttributes.create(LwM2mAttributes.GREATER_THAN)), //
Arguments.of("gt=60", LwM2mAttributes.create(LwM2mAttributes.GREATER_THAN, 60d)), //
Arguments.of("gt=-60", LwM2mAttributes.create(LwM2mAttributes.GREATER_THAN, -60d)), //
Arguments.of("gt=60.55", LwM2mAttributes.create(LwM2mAttributes.GREATER_THAN, 60.55)), //
Arguments.of("gt=-60.55", LwM2mAttributes.create(LwM2mAttributes.GREATER_THAN, -60.55)), //
Arguments.of("st", LwM2mAttributes.create(LwM2mAttributes.STEP)), //
Arguments.of("st=60", LwM2mAttributes.create(LwM2mAttributes.STEP, 60d)), //
Arguments.of("st=60.55", LwM2mAttributes.create(LwM2mAttributes.STEP, 60.55)) //
);
}
@ParameterizedTest(name = "Tests {index} : {0}")
@MethodSource("validAttributes")
public void check_one_valid_attribute(String attributeAsString, LwM2mAttribute<?> expectedValue)
throws LinkParseException, InvalidAttributeException {
LwM2mAttributeSet parsed = new LwM2mAttributeSet(parser.parseUriQuery(attributeAsString));
LwM2mAttributeSet attResult = new LwM2mAttributeSet(expectedValue);
assertEquals(parsed, attResult);
}
private static String[] invalidAttributes() {
return new String[] { //
// MINIMUM_PERIOD
"pmin=", //
"pmin=1.9", //
"pmin=-1.8", //
"pmin=a", //
// MAXIMUM_PERIOD
"pmax=", //
"pmax=-2", //
"pmax=2.7", //
"pmax=-2.6", //
"pmax=bc", //
// DIMENSION
"dim", //
"dim=", //
"dim=-3", //
"dim=3.5", //
"dim=-3.4", //
"dim=def", //
// EVALUATE_MINIMUM_PERIOD
"epmin=", //
"epmin=-4", //
"epmin=4.3", //
"epmin=-4.2", //
"epmin=ghij", //
// EVALUATE_MAXIMUM_PERIOD
"epmax=", //
"epmax=-5", //
"epmax=5.1", //
"epmax=-5.9", //
"epmax=klmno", //
// LESSER_THAN
"lt=", //
"lt=pqrts", //
"lt=0abc", //
// GREATER_THAN
"gt=", //
"gt=uvwxyz", //
"gt=0.xyz", //
// STEP
"st=", //
"st=-6", //
"st=-6.8", //
// Multi-attributes
"&pmin&pmax=60&gt=2&", //
"&pmin&pmax=60&gt=2", //
"pmin&pmax=60&gt=2&", //
"pmin&pmax=60&&gt=2", //
"pmin&pmax=60&&gt=2&&&", //
"&&&pmin&pmax=60&&gt=2", //
};
}
@ParameterizedTest(name = "Test {index} : {0}")
@MethodSource("invalidAttributes")
public void check_invalid_attributes(String invalidAttributesAsString) {
assertThrows(InvalidAttributeException.class,
() -> new LwM2mAttributeSet(parser.parseUriQuery(invalidAttributesAsString)));
}
@Test
public void check_multiple_valid_attributes() throws LinkParseException, InvalidAttributeException {
LwM2mAttributeSet parsed = new LwM2mAttributeSet(parser.parseUriQuery("pmin&pmax=60&gt=2"));
LwM2mAttributeSet attResult = new LwM2mAttributeSet( //
LwM2mAttributes.create(LwM2mAttributes.MINIMUM_PERIOD), //
LwM2mAttributes.create(LwM2mAttributes.MAXIMUM_PERIOD, 60l), //
LwM2mAttributes.create(LwM2mAttributes.GREATER_THAN, 2d));
assertEquals(attResult, parsed);
}
}

76
application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/DefaultLwM2mLinkParserTest.java

@ -0,0 +1,76 @@
/**
* Copyright © 2016-2023 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.attributes;
import org.eclipse.leshan.core.link.LinkParseException;
import org.eclipse.leshan.core.link.attributes.AttributeSet;
import org.eclipse.leshan.core.link.lwm2m.DefaultLwM2mLinkParser;
import org.eclipse.leshan.core.link.lwm2m.LwM2mLink;
import org.eclipse.leshan.core.link.lwm2m.LwM2mLinkParser;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
public class DefaultLwM2mLinkParserTest {
private final LwM2mLinkParser parser = new DefaultLwM2mLinkParser();
@Test
public void check_invalid_values() throws LinkParseException {
// first check it's OK with valid value
LwM2mLink[] parsed = parser.parseLwM2mLinkFromCoreLinkFormat("</3/0/11>;dim=255".getBytes(), null);
assertEquals(new LwM2mPath(3, 0, 11), parsed[0].getPath());
AttributeSet attResult = new LwM2mAttributeSet(LwM2mAttributes.create(LwM2mAttributes.DIMENSION, 255l));
assertEquals(attResult, parsed[0].getAttributes());
// then check an invalid one
assertThrowsExactly(LinkParseException.class, () -> {
// dim should be between 0-255
parser.parseLwM2mLinkFromCoreLinkFormat("</3/0/11>;dim=256".getBytes(), null);
});
// first check it's OK with valid value
parsed = parser.parseLwM2mLinkFromCoreLinkFormat("</0/1>;ssid=1".getBytes(), null);
assertEquals(new LwM2mPath(0, 1), parsed[0].getPath());
attResult = new LwM2mAttributeSet(LwM2mAttributes.create(LwM2mAttributes.SHORT_SERVER_ID, 1l));
assertEquals(attResult, parsed[0].getAttributes());
// then check an invalid one
assertThrowsExactly(LinkParseException.class, () -> {
// ssid should be between 1-65534
parser.parseLwM2mLinkFromCoreLinkFormat("</0/1>;ssid=0".getBytes(), null);
});
}
@Test
public void check_attribute_with_no_value_failed() throws LinkParseException {
// first check it's OK with value
LwM2mLink[] parsed = parser.parseLwM2mLinkFromCoreLinkFormat("</3/0/11>;pmin=200".getBytes(), null);
assertEquals(new LwM2mPath(3, 0, 11), parsed[0].getPath());
AttributeSet attResult = new LwM2mAttributeSet(LwM2mAttributes.create(LwM2mAttributes.MINIMUM_PERIOD, 200l));
assertEquals(attResult, parsed[0].getAttributes());
// then check an invalid one
assertThrowsExactly(LinkParseException.class, () -> {
// dim should be between 0-255
parser.parseLwM2mLinkFromCoreLinkFormat("</3/0/11>;pmin".getBytes(), null);
});
}
}

87
application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/LwM2mAttributesTest.java

@ -0,0 +1,87 @@
/**
* Copyright © 2016-2023 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.attributes;
import org.eclipse.leshan.core.link.attributes.InvalidAttributeException;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttribute;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeModel;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class LwM2mAttributesTest {
@ParameterizedTest(name = "Tests {index} : {0}")
@MethodSource("supportNullAttributes")
public void check_attribute_can_be_created_with_null_value(LwM2mAttributeModel<?> model)
throws InvalidAttributeException {
LwM2mAttribute<?> attribute = LwM2mAttributes.create(model);
assertNotNull(attribute);
assertFalse(attribute.hasValue());
assertNull(attribute.getValue());
attribute = LwM2mAttributes.create(model, null);
assertNotNull(attribute);
assertFalse(attribute.hasValue());
assertNull(attribute.getValue());
}
@ParameterizedTest(name = "Tests {index} : {0}")
@MethodSource("doesntSupportAttributesWithoutValue")
public void check_attribute_can_not_be_created_without_value(LwM2mAttributeModel<?> model) {
assertThrows(UnsupportedOperationException.class, () -> LwM2mAttributes.create(model));
}
@ParameterizedTest(name = "Tests {index} : {0}")
@MethodSource("doesntSupportAttributesWithValueNull")
public void check_attribute_can_not_be_created_with_null(LwM2mAttributeModel<?> model) {
assertThrows(NullPointerException.class, () -> LwM2mAttributes.create(model, null));
}
private static Stream<Arguments> supportNullAttributes() throws InvalidAttributeException {
return Stream.of(//
Arguments.of(LwM2mAttributes.MINIMUM_PERIOD), //
Arguments.of(LwM2mAttributes.MAXIMUM_PERIOD), //
Arguments.of(LwM2mAttributes.EVALUATE_MINIMUM_PERIOD), //
Arguments.of(LwM2mAttributes.EVALUATE_MAXIMUM_PERIOD), //
Arguments.of(LwM2mAttributes.LESSER_THAN), //
Arguments.of(LwM2mAttributes.GREATER_THAN), //
Arguments.of(LwM2mAttributes.STEP) //
);
}
private static Stream<Arguments> doesntSupportAttributesWithoutValue() throws InvalidAttributeException {
return Stream.of(//
Arguments.of(LwM2mAttributes.ENABLER_VERSION), //
Arguments.of(LwM2mAttributes.OBJECT_VERSION)//
);
}
private static Stream<Arguments> doesntSupportAttributesWithValueNull() throws InvalidAttributeException {
return Stream.of(//
Arguments.of(LwM2mAttributes.DIMENSION), //
Arguments.of(LwM2mAttributes.SHORT_SERVER_ID) //
);
}
}

20
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java

@ -17,9 +17,9 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.response.WriteResponse;
@ -45,7 +45,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
private final AtomicInteger updateResult = new AtomicInteger(0); private final AtomicInteger updateResult = new AtomicInteger(0);
@Override @Override
public ReadResponse read(ServerIdentity identity, int resourceId) { public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
if (!identity.isSystem()) if (!identity.isSystem())
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
@ -65,24 +65,24 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
} }
@Override @Override
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
String withParams = null; String withArguments = "";
if (params != null && params.length() != 0) { if (!arguments.isEmpty())
withParams = " with params " + params; withArguments = " with arguments " + arguments;
} log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withArguments);
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : "");
switch (resourceId) { switch (resourceId) {
case 2: case 2:
startUpdating(); startUpdating();
return ExecuteResponse.success(); return ExecuteResponse.success();
default: default:
return super.execute(identity, resourceId, params); return super.execute(identity, resourceId, arguments);
} }
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { public WriteResponse write(org.eclipse.leshan.client.servers.LwM2mServer identity, boolean replace, int resourceId, LwM2mResource value) {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {

243
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java

@ -18,17 +18,28 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.client.californium.LeshanClient; import org.eclipse.leshan.client.LeshanClient;
import org.eclipse.leshan.client.californium.LeshanClientBuilder; import org.eclipse.leshan.client.LeshanClientBuilder;
import org.eclipse.leshan.client.californium.endpoint.CaliforniumClientEndpointFactory;
import org.eclipse.leshan.client.californium.endpoint.CaliforniumClientEndpointsProvider;
import org.eclipse.leshan.client.californium.endpoint.ClientProtocolProvider;
import org.eclipse.leshan.client.californium.endpoint.coap.CoapOscoreProtocolProvider;
import org.eclipse.leshan.client.californium.endpoint.coaps.CoapsClientEndpointFactory;
import org.eclipse.leshan.client.californium.endpoint.coaps.CoapsClientProtocolProvider;
import org.eclipse.leshan.client.endpoint.LwM2mClientEndpointsProvider;
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler; import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.servers.ServerIdentity; import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter;
import org.eclipse.leshan.client.send.ManualDataSender;
import org.eclipse.leshan.client.servers.LwM2mServer;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
@ -49,13 +60,13 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.DEVICE;
import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE;
@ -63,6 +74,10 @@ import static org.eclipse.leshan.core.LwM2mId.LOCATION;
import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.serverId;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.serverIdBs;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerIdBs0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE;
@ -97,11 +112,6 @@ public class LwM2MTestClient {
private final ScheduledExecutorService executor; private final ScheduledExecutorService executor;
private final String endpoint; private final String endpoint;
private LeshanClient leshanClient; private LeshanClient leshanClient;
private Security lwm2mSecurity;
private Security lwm2mSecurityBs;
private Lwm2mServer lwm2mServer;
private Lwm2mServer lwm2mServerBs;
private SimpleLwM2MDevice lwM2MDevice; private SimpleLwM2MDevice lwM2MDevice;
private FwLwM2MDevice fwLwM2MDevice; private FwLwM2MDevice fwLwM2MDevice;
private SwLwM2MDevice swLwM2MDevice; private SwLwM2MDevice swLwM2MDevice;
@ -112,8 +122,7 @@ public class LwM2MTestClient {
private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
private LwM2mClientContext clientContext; private LwM2mClientContext clientContext;
public void init(Security security, Configuration coapConfig, int port, boolean isRpc, boolean isBootstrap, public void init(Security security, Security securityBs,Configuration coapConfig, int port, boolean isRpc,
int shortServerId, int shortServerIdBs, Security securityBs,
LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler,
LwM2mClientContext clientContext) throws InvalidDDFFileException, IOException { LwM2mClientContext clientContext) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", leshanClient); Assert.assertNull("client already initialized", leshanClient);
@ -125,30 +134,36 @@ public class LwM2MTestClient {
} }
LwM2mModel model = new StaticModel(models); LwM2mModel model = new StaticModel(models);
ObjectsInitializer initializer = new ObjectsInitializer(model); ObjectsInitializer initializer = new ObjectsInitializer(model);
if (securityBs == null) {
initializer.setInstancesForObject(SECURITY, this.lwm2mSecurity = security); if (securityBs != null && security != null) {
} else { // SECURITY
securityBs.setId(0); security.setId(serverId);
security.setId(1); securityBs.setId(serverIdBs);
LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{this.lwm2mSecurityBs = securityBs, this.lwm2mSecurity = security}; LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{securityBs, security};
initializer.setClassForObject(SECURITY, Security.class); initializer.setClassForObject(SECURITY, Security.class);
initializer.setInstancesForObject(SECURITY, instances); initializer.setInstancesForObject(SECURITY, instances);
} // SERVER
if (isBootstrap) { Server lwm2mServer = new Server(shortServerId, 300);
initializer.setInstancesForObject(SERVER, lwm2mServerBs = new Lwm2mServer(shortServerIdBs, 300)); lwm2mServer.setId(serverId);
Server serverBs = new Server(shortServerIdBs0, 300);
serverBs.setId(serverIdBs);
instances = new LwM2mInstanceEnabler[]{serverBs, lwm2mServer};
initializer.setClassForObject(SERVER, Server.class);
initializer.setInstancesForObject(SERVER, instances);
} else if (securityBs != null) {
// SECURITY
initializer.setInstancesForObject(SECURITY, securityBs);
// SERVER
initializer.setClassForObject(SERVER, Server.class);
} else { } else {
if (securityBs == null) { // SECURITY
initializer.setInstancesForObject(SERVER, lwm2mServer = new Lwm2mServer(shortServerId, 300)); initializer.setInstancesForObject(SECURITY, security);
} else { // SERVER
lwm2mServerBs = new Lwm2mServer(shortServerIdBs, 300); Server lwm2mServer = new Server(shortServerId, 300);
lwm2mServerBs.setId(0); lwm2mServer.setId(serverId);
lwm2mServer = new Lwm2mServer(shortServerId, 300); initializer.setInstancesForObject(SERVER, lwm2mServer );
lwm2mServer.setId(1);
LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{lwm2mServerBs, lwm2mServer};
initializer.setClassForObject(SERVER, Server.class);
initializer.setInstancesForObject(SERVER, instances);
}
} }
initializer.setInstancesForObject(DEVICE, lwM2MDevice = new SimpleLwM2MDevice(executor)); initializer.setInstancesForObject(DEVICE, lwM2MDevice = new SimpleLwM2MDevice(executor));
initializer.setInstancesForObject(FIRMWARE, fwLwM2MDevice = new FwLwM2MDevice()); initializer.setInstancesForObject(FIRMWARE, fwLwM2MDevice = new FwLwM2MDevice());
initializer.setInstancesForObject(SOFTWARE_MANAGEMENT, swLwM2MDevice = new SwLwM2MDevice()); initializer.setInstancesForObject(SOFTWARE_MANAGEMENT, swLwM2MDevice = new SwLwM2MDevice());
@ -160,106 +175,193 @@ public class LwM2MTestClient {
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0));
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0), new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12)); initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0), new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12));
List<LwM2mObjectEnabler> enablers = initializer.createAll();
// Create Californium Endpoints Provider:
// --------------------------------------
// Define Custom CoAPS protocol provider
CoapsClientProtocolProvider customCoapsProtocolProvider = new CoapsClientProtocolProvider() {
@Override
public CaliforniumClientEndpointFactory createDefaultEndpointFactory() {
return new CoapsClientEndpointFactory() {
@Override
protected DtlsConnectorConfig.Builder createRootDtlsConnectorConfigBuilder(
Configuration configuration) {
DtlsConnectorConfig.Builder builder = super.createRootDtlsConnectorConfigBuilder(configuration);
return builder;
};
};
}
};
// Create client endpoints Provider
List<ClientProtocolProvider> protocolProvider = new ArrayList<>();
/**
* "Use java-coap for CoAP protocol instead of Californium."
*/
boolean useJavaCoap = false;
if (!useJavaCoap) protocolProvider.add(new CoapOscoreProtocolProvider());
protocolProvider.add(customCoapsProtocolProvider);
CaliforniumClientEndpointsProvider.Builder endpointsBuilder = new CaliforniumClientEndpointsProvider.Builder(
protocolProvider.toArray(new ClientProtocolProvider[protocolProvider.size()]));
// Create Californium Configuration
Configuration clientCoapConfig = endpointsBuilder.createDefaultConfiguration();
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(coapConfig); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(coapConfig);
dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, true);
// Set some DTLS stuff
// These configuration values are always overwritten by CLI therefore set them to transient.
clientCoapConfig.setTransient(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY);
clientCoapConfig.setTransient(DtlsConfig.DTLS_CONNECTION_ID_LENGTH);
boolean supportDeprecatedCiphers = false;
clientCoapConfig.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, !supportDeprecatedCiphers);
/**
* "Control usage of DTLS connection ID.", //
* "- 'on' to activate Connection ID support (same as -cid 0)", //
* "- 'off' to deactivate it", //
* "- Positive value define the size in byte of CID generated.", //
* "- 0 value means we accept to use CID but will not generated one for foreign peer.", //
* "Default: off"
*/
Integer cid = null;
clientCoapConfig.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, cid);
// Set Californium Configuration
endpointsBuilder.setConfiguration(clientCoapConfig);
endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress());
// creates EndpointsProvider
List<LwM2mClientEndpointsProvider> endpointsProvider = new ArrayList<>();
endpointsProvider.add(endpointsBuilder.build());
/**
* dependency -> org.eclipse.leshan.transport.javacoap.client.endpoint;
*/
// if (useJavaCoap) {endpointsProvider.add(new JavaCoapClientEndpointsProvider());
// Configure Registration Engine
DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory(); DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory();
engineFactory.setReconnectOnUpdate(false); // old
/**
* Force reconnection/rehandshake on registration update.
*/
int comPeriodInSec = 5;
if (comPeriodInSec > 0) engineFactory.setCommunicationPeriod(comPeriodInSec * 1000);
// engineFactory.setCommunicationPeriod(5000); // old
/**
* By default client will try to resume DTLS session by using abbreviated Handshake. This option force to always do a full handshake."
*/
boolean reconnectOnUpdate = false;
engineFactory.setReconnectOnUpdate(reconnectOnUpdate);
// engineFactory.setReconnectOnUpdate(false); // old
engineFactory.setResumeOnConnect(true); engineFactory.setResumeOnConnect(true);
engineFactory.setCommunicationPeriod(5000); // new
/**
* Client use queue mode (not fully implemented).
*/
boolean queueMode = false;
engineFactory.setQueueMode(queueMode);
// Create client
LeshanClientBuilder builder = new LeshanClientBuilder(endpoint); LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
builder.setLocalAddress("0.0.0.0", port); builder.setObjects(enablers);
builder.setObjects(initializer.createAll()); builder.setEndpointsProviders(endpointsProvider.toArray(new LwM2mClientEndpointsProvider[endpointsProvider.size()]));
builder.setCoapConfig(coapConfig); builder.setDataSenders(new ManualDataSender());
builder.setDtlsConfig(dtlsConfig); builder.setRegistrationEngineFactory(engineFactory);
boolean supportOldFormat = true;
if (supportOldFormat) {
builder.setDecoder(new DefaultLwM2mDecoder(supportOldFormat));
builder.setEncoder(new DefaultLwM2mEncoder(new LwM2mValueConverterImpl(), supportOldFormat));
}
builder.setRegistrationEngineFactory(engineFactory); builder.setRegistrationEngineFactory(engineFactory);
builder.setSharedExecutor(executor); builder.setSharedExecutor(executor);
builder.setDecoder(new DefaultLwM2mDecoder(false));
builder.setEncoder(new DefaultLwM2mEncoder(new LwM2mValueConverterImpl(), false));
clientStates = new HashSet<>(); clientStates = new HashSet<>();
clientStates.add(ON_INIT); clientStates.add(ON_INIT);
leshanClient = builder.build(); leshanClient = builder.build();
LwM2mClientObserver observer = new LwM2mClientObserver() { LwM2mClientObserver observer = new LwM2mClientObserver() {
@Override @Override
public void onBootstrapStarted(ServerIdentity bsserver, BootstrapRequest request) { public void onBootstrapStarted(LwM2mServer bsserver, BootstrapRequest request) {
clientStates.add(ON_BOOTSTRAP_STARTED); clientStates.add(ON_BOOTSTRAP_STARTED);
} }
@Override @Override
public void onBootstrapSuccess(ServerIdentity bsserver, BootstrapRequest request) { public void onBootstrapSuccess(LwM2mServer bsserver, BootstrapRequest request) {
clientStates.add(ON_BOOTSTRAP_SUCCESS); clientStates.add(ON_BOOTSTRAP_SUCCESS);
} }
@Override @Override
public void onBootstrapFailure(ServerIdentity bsserver, BootstrapRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { public void onBootstrapFailure(LwM2mServer bsserver, BootstrapRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
clientStates.add(ON_BOOTSTRAP_FAILURE); clientStates.add(ON_BOOTSTRAP_FAILURE);
} }
@Override @Override
public void onBootstrapTimeout(ServerIdentity bsserver, BootstrapRequest request) { public void onBootstrapTimeout(LwM2mServer bsserver, BootstrapRequest request) {
clientStates.add(ON_BOOTSTRAP_TIMEOUT); clientStates.add(ON_BOOTSTRAP_TIMEOUT);
} }
@Override @Override
public void onRegistrationStarted(ServerIdentity server, RegisterRequest request) { public void onRegistrationStarted(LwM2mServer server, RegisterRequest request) {
clientStates.add(ON_REGISTRATION_STARTED); clientStates.add(ON_REGISTRATION_STARTED);
} }
@Override @Override
public void onRegistrationSuccess(ServerIdentity server, RegisterRequest request, String registrationID) { public void onRegistrationSuccess(LwM2mServer server, RegisterRequest request, String registrationID) {
clientStates.add(ON_REGISTRATION_SUCCESS); clientStates.add(ON_REGISTRATION_SUCCESS);
} }
@Override @Override
public void onRegistrationFailure(ServerIdentity server, RegisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { public void onRegistrationFailure(LwM2mServer server, RegisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
clientStates.add(ON_REGISTRATION_FAILURE); clientStates.add(ON_REGISTRATION_FAILURE);
} }
@Override @Override
public void onRegistrationTimeout(ServerIdentity server, RegisterRequest request) { public void onRegistrationTimeout(LwM2mServer server, RegisterRequest request) {
clientStates.add(ON_REGISTRATION_TIMEOUT); clientStates.add(ON_REGISTRATION_TIMEOUT);
} }
@Override @Override
public void onUpdateStarted(ServerIdentity server, UpdateRequest request) { public void onUpdateStarted(LwM2mServer server, UpdateRequest request) {
clientStates.add(ON_UPDATE_STARTED); clientStates.add(ON_UPDATE_STARTED);
} }
@Override @Override
public void onUpdateSuccess(ServerIdentity server, UpdateRequest request) { public void onUpdateSuccess(LwM2mServer server, UpdateRequest request) {
clientStates.add(ON_UPDATE_SUCCESS); clientStates.add(ON_UPDATE_SUCCESS);
} }
@Override @Override
public void onUpdateFailure(ServerIdentity server, UpdateRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { public void onUpdateFailure(LwM2mServer server, UpdateRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
clientStates.add(ON_UPDATE_FAILURE); clientStates.add(ON_UPDATE_FAILURE);
} }
@Override @Override
public void onUpdateTimeout(ServerIdentity server, UpdateRequest request) { public void onUpdateTimeout(LwM2mServer server, UpdateRequest request) {
clientStates.add(ON_UPDATE_TIMEOUT); clientStates.add(ON_UPDATE_TIMEOUT);
} }
@Override @Override
public void onDeregistrationStarted(ServerIdentity server, DeregisterRequest request) { public void onDeregistrationStarted(LwM2mServer server, DeregisterRequest request) {
clientStates.add(ON_DEREGISTRATION_STARTED); clientStates.add(ON_DEREGISTRATION_STARTED);
} }
@Override @Override
public void onDeregistrationSuccess(ServerIdentity server, DeregisterRequest request) { public void onDeregistrationSuccess(LwM2mServer server, DeregisterRequest request) {
clientStates.add(ON_DEREGISTRATION_SUCCESS); clientStates.add(ON_DEREGISTRATION_SUCCESS);
} }
@Override @Override
public void onDeregistrationFailure(ServerIdentity server, DeregisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { public void onDeregistrationFailure(LwM2mServer server, DeregisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
clientStates.add(ON_DEREGISTRATION_FAILURE); clientStates.add(ON_DEREGISTRATION_FAILURE);
} }
@Override @Override
public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) { public void onDeregistrationTimeout(LwM2mServer server, DeregisterRequest request) {
clientStates.add(ON_DEREGISTRATION_TIMEOUT); clientStates.add(ON_DEREGISTRATION_TIMEOUT);
} }
@ -270,6 +372,21 @@ public class LwM2MTestClient {
}; };
this.leshanClient.addObserver(observer); this.leshanClient.addObserver(observer);
// Add some log about object tree life cycle.
this.leshanClient.getObjectTree().addListener(new ObjectsListenerAdapter() {
@Override
public void objectRemoved(LwM2mObjectEnabler object) {
log.info("Object {} v{} disabled.", object.getId(), object.getObjectModel().version);
}
@Override
public void objectAdded(LwM2mObjectEnabler object) {
log.info("Object {} v{} enabled.", object.getId(), object.getObjectModel().version);
}
});
if (!isRpc) { if (!isRpc) {
this.start(true); this.start(true);
} }
@ -279,18 +396,6 @@ public class LwM2MTestClient {
if (leshanClient != null) { if (leshanClient != null) {
leshanClient.destroy(true); leshanClient.destroy(true);
} }
if (lwm2mSecurityBs != null) {
lwm2mSecurityBs = null;
}
if (lwm2mSecurity != null) {
lwm2mSecurity = null;
}
if (lwm2mServerBs != null) {
lwm2mServerBs = null;
}
if (lwm2mServer != null) {
lwm2mServer = null;
}
if (lwM2MDevice != null) { if (lwM2MDevice != null) {
lwM2MDevice.destroy(); lwM2MDevice.destroy();
} }

5
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java

@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mMultipleResource;
@ -93,7 +92,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
} }
@Override @Override
public ReadResponse read(ServerIdentity identity, int resourceId) { public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
try { try {
switch (resourceId) { switch (resourceId) {
case 0: case 0:
@ -118,7 +117,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { public WriteResponse write(org.eclipse.leshan.client.servers.LwM2mServer identity, boolean replace, int resourceId, LwM2mResource value) {
log.info("Write on Device resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); log.info("Write on Device resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
case 0: case 0:

3
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java

@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
@ -85,7 +84,7 @@ public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable {
} }
@Override @Override
public ReadResponse read(ServerIdentity identity, int resourceId) { public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
log.info("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); log.info("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
case 0: case 0:

8
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java

@ -17,8 +17,8 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
@ -56,7 +56,7 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
} }
@Override @Override
public synchronized ReadResponse read(ServerIdentity identity, int resourceId) { public synchronized ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
log.info("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); log.info("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
case 5601: case 5601:
@ -73,14 +73,14 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
} }
@Override @Override
public synchronized ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { public synchronized ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
log.info("Execute on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); log.info("Execute on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
case 5605: case 5605:
resetMinMaxMeasuredValues(); resetMinMaxMeasuredValues();
return ExecuteResponse.success(); return ExecuteResponse.success();
default: default:
return super.execute(identity, resourceId, params); return super.execute(identity, resourceId, arguments);
} }
} }

11
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/Lwm2mServer.java

@ -18,11 +18,11 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel.Type; import org.eclipse.leshan.core.model.ResourceModel.Type;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.request.BindingMode; import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.response.WriteResponse;
@ -70,7 +70,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
} }
@Override @Override
public ReadResponse read(ServerIdentity identity, int resourceid) { public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceid) {
if (!identity.isSystem()) if (!identity.isSystem())
LOG.debug("Read on Server resource /{}/{}/{}", getModel().id, getId(), resourceid); LOG.debug("Read on Server resource /{}/{}/{}", getModel().id, getId(), resourceid);
@ -108,7 +108,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceid, LwM2mResource value) { public WriteResponse write(org.eclipse.leshan.client.servers.LwM2mServer identity, boolean replace, int resourceid, LwM2mResource value) {
if (!identity.isSystem()) if (!identity.isSystem())
log.debug("Write on Server resource /{}/{}/{}", getModel().id, getId(), resourceid); log.debug("Write on Server resource /{}/{}/{}", getModel().id, getId(), resourceid);
@ -195,8 +195,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
} }
} }
@Override public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceid, Arguments arguments) {
public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
log.info("Execute on Server resource /{}/{}/{}", getModel().id, getId(), resourceid); log.info("Execute on Server resource /{}/{}/{}", getModel().id, getId(), resourceid);
if (resourceid == 8) { if (resourceid == 8) {
getLwM2mClient().triggerRegistrationUpdate(identity); getLwM2mClient().triggerRegistrationUpdate(identity);
@ -210,7 +209,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
return ExecuteResponse.badRequest("probably no bootstrap server configured"); return ExecuteResponse.badRequest("probably no bootstrap server configured");
} }
} else { } else {
return super.execute(identity, resourceid, params); return super.execute(identity, resourceid, arguments);
} }
} }

17
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java

@ -17,11 +17,11 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.Destroyable; import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.response.WriteResponse;
@ -66,7 +66,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
@Override @Override
public ReadResponse read(ServerIdentity identity, int resourceId) { public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
if (!identity.isSystem()) if (!identity.isSystem())
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
@ -108,17 +108,16 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
} }
@Override @Override
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
String withParams = null; String withArguments = "";
if (params != null && params.length() != 0) { if (!arguments.isEmpty())
withParams = " with params " + params; withArguments = " with arguments " + arguments;
} log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withArguments);
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : "");
return ExecuteResponse.success(); return ExecuteResponse.success();
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { public WriteResponse write(org.eclipse.leshan.client.servers.LwM2mServer identity, boolean replace, int resourceId, LwM2mResource value) {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {

19
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java

@ -17,9 +17,9 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.response.WriteResponse;
@ -45,7 +45,7 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
private final AtomicInteger updateResult = new AtomicInteger(0); private final AtomicInteger updateResult = new AtomicInteger(0);
@Override @Override
public ReadResponse read(ServerIdentity identity, int resourceId) { public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
if (!identity.isSystem()) if (!identity.isSystem())
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
@ -63,12 +63,11 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
} }
@Override @Override
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
String withParams = null; String withArguments = "";
if (params != null && params.length() != 0) { if (!arguments.isEmpty())
withParams = " with params " + params; withArguments = " with arguments " + arguments;
} log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withArguments);
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : "");
switch (resourceId) { switch (resourceId) {
case 4: case 4:
@ -77,12 +76,12 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
case 6: case 6:
return ExecuteResponse.success(); return ExecuteResponse.success();
default: default:
return super.execute(identity, resourceId, params); return super.execute(identity, resourceId, arguments);
} }
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { public WriteResponse write(org.eclipse.leshan.client.servers.LwM2mServer identity, boolean replace, int resourceId, LwM2mResource value) {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {

12
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java

@ -99,8 +99,8 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration); createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO));
final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO); final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
createNewClient(SECURITY_NO_SEC, COAP_CONFIG, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, false, null); createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
awaitObserveReadAll(0, false, device.getId().getId().toString()); awaitObserveReadAll(0, device.getId().getId().toString());
device.setFirmwareId(createFirmware().getId()); device.setFirmwareId(createFirmware().getId());
final Device savedDevice = doPost("/api/device", device, Device.class); final Device savedDevice = doPost("/api/device", device, Device.class);
@ -124,8 +124,8 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration); createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5));
final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA5); final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA5);
createNewClient(SECURITY_NO_SEC, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA5, false, null); createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA5);
awaitObserveReadAll(9, false, device.getId().getId().toString()); awaitObserveReadAll(9, device.getId().getId().toString());
device.setFirmwareId(createFirmware().getId()); device.setFirmwareId(createFirmware().getId());
final Device savedDevice = doPost("/api/device", device, Device.class); final Device savedDevice = doPost("/api/device", device, Device.class);
@ -154,8 +154,8 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration); createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9));
final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA9); final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA9);
createNewClient(SECURITY_NO_SEC, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA9, false, null); createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA9);
awaitObserveReadAll(9, false, device.getId().getId().toString()); awaitObserveReadAll(9, device.getId().getId().toString());
device.setSoftwareId(createSoftware().getId()); device.setSoftwareId(createSoftware().getId());
final Device savedDevice = doPost("/api/device", device, Device.class); //sync call final Device savedDevice = doPost("/api/device", device, Device.class); //sync call

7
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java

@ -15,6 +15,8 @@
*/ */
package org.thingsboard.server.transport.lwm2m.rpc; package org.thingsboard.server.transport.lwm2m.rpc;
import org.eclipse.leshan.core.link.LinkParser;
import org.eclipse.leshan.core.link.lwm2m.DefaultLwM2mLinkParser;
import org.junit.Before; import org.junit.Before;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials;
@ -50,6 +52,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources;
@DaoSqlTest @DaoSqlTest
public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final LinkParser linkParser = new DefaultLwM2mLinkParser();
protected String OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC; protected String OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC;
protected String deviceId; protected String deviceId;
public Set expectedObjects; public Set expectedObjects;
@ -84,7 +87,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
private void initRpc () throws Exception { private void initRpc () throws Exception {
String endpoint = DEVICE_ENDPOINT_RPC_PREF + endpointSequence.incrementAndGet(); String endpoint = DEVICE_ENDPOINT_RPC_PREF + endpointSequence.incrementAndGet();
createNewClient(SECURITY_NO_SEC, COAP_CONFIG, true, endpoint, false, null); createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, true, endpoint);
expectedObjects = ConcurrentHashMap.newKeySet(); expectedObjects = ConcurrentHashMap.newKeySet();
expectedObjectIdVers = ConcurrentHashMap.newKeySet(); expectedObjectIdVers = ConcurrentHashMap.newKeySet();
expectedInstances = ConcurrentHashMap.newKeySet(); expectedInstances = ConcurrentHashMap.newKeySet();
@ -147,7 +150,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
deviceId = device.getId().getId().toString(); deviceId = device.getId().getId().toString();
lwM2MTestClient.start(true); lwM2MTestClient.start(true);
awaitObserveReadAll(2, false, device.getId().getId().toString()); // awaitObserveReadAll(2, true, device.getId().getId().toString());
} }
protected String pathIdVerToObjectId(String pathIdVer) { protected String pathIdVerToObjectId(String pathIdVer) {

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java

@ -99,7 +99,7 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
String actualResult = sendRPCreateById(expectedPath, expectedValue); String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expectedObjectId = pathIdVerToObjectId((String) expectedPath); String expectedObjectId = pathIdVerToObjectId(expectedPath);
LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId);
String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!";
String actual = rpcActualResult.get("error").asText(); String actual = rpcActualResult.get("error").asText();
@ -118,7 +118,7 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
String actualResult = sendRPCreateById(expectedPath, expectedValue); String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expectedObjectId = pathIdVerToObjectId((String) expectedPath); String expectedObjectId = pathIdVerToObjectId(expectedPath);
LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId);
String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!";
String actual = rpcActualResult.get("error").asText(); String actual = rpcActualResult.get("error").asText();

17
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java

@ -18,6 +18,8 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.link.LinkParseException;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -52,12 +54,17 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration
Set actualObjects = ConcurrentHashMap.newKeySet(); Set actualObjects = ConcurrentHashMap.newKeySet();
Set actualInstances = ConcurrentHashMap.newKeySet(); Set actualInstances = ConcurrentHashMap.newKeySet();
rpcActualValue.forEach(node -> { rpcActualValue.forEach(node -> {
if (!node.get("uriReference").asText().equals("/")) { try {
LwM2mPath path = new LwM2mPath(node.get("uriReference").asText()); Link[] parsedLink = linkParser.parseCoreLinkFormat(node.asText().getBytes());
actualObjects.add("/" + path.getObjectId()); if (!parsedLink[0].getUriReference().equals("/")) {
if (path.isObjectInstance()) { LwM2mPath path = new LwM2mPath(parsedLink[0].getUriReference());
actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId()); actualObjects.add("/" + path.getObjectId());
if (path.isObjectInstance()) {
actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId());
}
} }
} catch (LinkParseException e) {
throw new RuntimeException(e);
} }
}); });
assertEquals(expectedInstances, actualInstances); assertEquals(expectedInstances, actualInstances);

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java

@ -168,12 +168,14 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
*/ */
@Test @Test
public void testObserveReadAll_Result_CONTENT_Value_Contains_Paths_Count_ObserveReadAll() throws Exception { public void testObserveReadAll_Result_CONTENT_Value_Contains_Paths_Count_ObserveReadAll() throws Exception {
String expectedId = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14;
sendRpcObserve("Observe", expectedId);
String actualResultReadAll = sendRpcObserve("ObserveReadAll", null); String actualResultReadAll = sendRpcObserve("ObserveReadAll", null);
ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class); ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText()); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText());
String actualValuesReadAll = rpcActualResultReadAll.get("value").asText(); String actualValuesReadAll = rpcActualResultReadAll.get("value").asText();
log.warn("ObserveReadAll: [{}]", actualValuesReadAll); log.warn("ObserveReadAll: [{}]", actualValuesReadAll);
assertEquals(2, actualValuesReadAll.split(",").length); assertEquals(3, actualValuesReadAll.split(",").length);
} }

36
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java

@ -50,22 +50,28 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
*/ */
@Test @Test
public void testReadAllObjectsInClientById_Result_CONTENT_Value_IsLwM2mObject_IsInstances() throws Exception { public void testReadAllObjectsInClientById_Result_CONTENT_Value_IsLwM2mObject_IsInstances() throws Exception {
expectedObjectIdVers.forEach(expected -> { try {
try { expectedObjectIdVers.forEach(expected -> {
String actualResult = sendRPCById((String) expected); try {
String expectedObjectId = pathIdVerToObjectId((String) expected); String actualResult = sendRPCById((String) expected);
LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); String expectedObjectId = pathIdVerToObjectId((String) expected);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); LwM2mPath expectedPath = new LwM2mPath(expectedObjectId);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources="; assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
if (expectedPath.getObjectId() == 2) { String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources=";
expectedObjectInstances = "LwM2mObject [id=2, instances={}]"; if (expectedPath.getObjectId() == 1) {
expectedObjectInstances = "LwM2mObject [id=1, instances={1=";
} else if (expectedPath.getObjectId() == 2) {
expectedObjectInstances = "LwM2mObject [id=2, instances={}]";
}
assertTrue(rpcActualResult.get("value").asText().contains(expectedObjectInstances));
} catch (Exception e) {
e.printStackTrace();
} }
assertTrue(rpcActualResult.get("value").asText().contains(expectedObjectInstances)); });
} catch (Exception e) { } catch (Exception e2){
e.printStackTrace(); e2.printStackTrace();
} }
});
} }
/** /**

8
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest;
@ -29,10 +30,11 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID
public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MIntegrationTest { public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MIntegrationTest {
@Ignore
/** /**
* WriteAttributes {"id":"/3/0/14","attributes":{"pmax":100, "pmin":10}} * WriteAttributes {"id":"/3/0/14","attributes":{"pmax":100, "pmin":10}}
* if not implemented: * if not implemented:
* {"result":"INTERNAL_SERVER_ERROR","error":"not implemented"} * {"result":"BAD_REQUEST","error":"Content Format is mandatory"}
* if implemented: * if implemented:
* {"result":"CHANGED"} * {"result":"CHANGED"}
*/ */
@ -42,8 +44,8 @@ public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MInte
String expectedValue = "{\"pmax\":100, \"pmin\":10}"; String expectedValue = "{\"pmax\":100, \"pmin\":10}";
String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.INTERNAL_SERVER_ERROR.getName(), rpcActualResult.get("result").asText()); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expected = "not implemented"; String expected = "Content Format is mandatory";
String actual = rpcActualResult.get("error").asText(); String actual = rpcActualResult.get("error").asText();
assertTrue(actual.equals(expected)); assertTrue(actual.equals(expected));
} }

67
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java

@ -161,8 +161,8 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
int resourceInstanceId25 = 25; int resourceInstanceId25 = 25;
String expectedValue0 = "00ad45675600"; String expectedValue0 = "00ad45675600";
String expectedValue25 = "25ad45675600cdef"; String expectedValue25 = "25ad45675600cdef";
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}"; String expectedValueResourcesObject19 = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValueResourcesObject19);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
String expectedPath0 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0; String expectedPath0 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0;
@ -181,29 +181,37 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
/** /**
* ResourceInstance + KeySingleResource + IdSingleResource * ResourceInstance + KeySingleResource + IdSingleResource
* WriteComposite {"nodes":{"/19/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}} * WriteComposite {"/19_1.1/0":{"0":{"0":"00ad45675600", "25":"25ad45675600cdef"}}, "UtfOffset":"+04", "/3_1.0/0/15":"Kiyv/Europe"}
* {"result":"CHANGED"} * {"result":"CHANGED"}
*/ */
@Test @Test
public void testWriteCompositeValueSingleResourceResourceInstanceByIdKey_Result_CHANGED() throws Exception { public void testWriteCompositeValueSingleResourceWithMultiResourceInstanceByIdKey_Result_CHANGED() throws Exception {
int resourceInstanceId2 = 2; String expectedPath_19_0 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0;
String expectedPath19_1_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId2; int resourceInstanceId0 = 0;
String expectedValue19_1_0_2 = "00001234"; int resourceInstanceId25 = 25;
String expectedValue0 = "00ad45675600";
String expectedValue25 = "25ad45675600cdef";
String expectedValue_19_Resources = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14; String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14;
String expectedValue3_0_14 = "+04"; String expectedValue3_0_14 = "+04";
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15; String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15;
String expectedValue3_0_15 = "Kiyv/Europe"; String expectedValue3_0_15 = "Kiyv/Europe";
String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + String nodes = "{\"" + expectedPath_19_0 + "\":" + expectedValue_19_Resources + ", \"" + expectedKey3_0_14 +
"\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}";
String actualResult = sendCompositeRPC(nodes); String actualResult = sendCompositeRPC(nodes);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath19_1_0_2); actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText(); String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected)); assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadByKey(expectedKey3_0_14); actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadByKey(expectedKey3_0_14);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText(); actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
@ -217,29 +225,36 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
/** /**
* multipleResource == error * multipleResource == error
* bad - cannot be used for value Json, only primitive: SingleResource, ResourceInstance (for Json: WriteUpdate, WriteReplace) * WriteComposite {"/19_1.1/0/0":{"0":"00ad45675600", "25":"25ad45675600cdef"}}
* WriteComposite {"nodes":{"/19/0/0":{"0":"abcd5678", "10":"abcd5678"}}} * {"result":"CHANGED"}
*/ */
@Test @Test
public void testWriteCompositeValueSingleMultipleResourceByIdKey_Result_BAD_REQUEST_WriteComposite_operation_for_SingleResources_or_and_ResourceInstance() throws Exception { public void testWriteCompositeValueSingleMultipleResourceOpaqueValueInputHexStringByIdKey_Result_CHANGED() throws Exception {
String nodes = "{\"/19/0/0\":{\"0\":\"abcd5678\", \"10\":\"abcd5678\"}}"; String expectedPath_19_0 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0;
int resourceInstanceId0 = 0;
int resourceInstanceId25 = 25;
String expectedValue0 = "00ad45675600";
String expectedValue25 = "25ad45675600cdef";
String nodes = "{\"" + expectedPath_19_0 + "/" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String actualResult = sendCompositeRPC(nodes); String actualResult = sendCompositeRPC(nodes);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
String actualValues = rpcActualResult.get("error").asText(); actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0);
String expectedNodes = nodes.replaceAll("\"", "").replaceAll(":", "="); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String expected = String.format("nodes: %s is not validate value. " + String actualValues = rpcActualResult.get("value").asText();
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", expectedNodes); String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
assertEquals(expected, actualValues); assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
} }
/** /**
* update_resourceInstances&update_singleResource * update_resourceInstances&update_singleResource
* new ResourceInstance if Resource is Multiple & Resource Single
* - WriteReplace {"id":"/19_1.2/1/0","value":{"2":ddff12"}}
* - WriteReplace {"key":"UtfOffset","value":"+04"}
* - WriteReplace {"id":"/3/0/15","value":"Kiyv/Europe"}
* WriteComposite {"nodes":{"/19_1.1/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}}} * WriteComposite {"nodes":{"/19_1.1/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}}}
* {"result":"CHANGED"} * {"result":"CHANGED"}
*/ */
@ -331,4 +346,4 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
String setRpcRequest = "{\"method\": \"WriteComposite\", \"params\": {\"nodes\":" + nodes + "}}"; String setRpcRequest = "{\"method\": \"WriteComposite\", \"params\": {\"nodes\":" + nodes + "}}";
return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk());
} }
} }

35
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java

@ -63,7 +63,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await; import static org.awaitility.Awaitility.await;
import static org.eclipse.leshan.client.object.Security.noSecBootstap; import static org.eclipse.leshan.client.object.Security.noSecBootstrap;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_DEREGISTRATION_STARTED; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_DEREGISTRATION_STARTED;
@ -89,6 +89,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
protected static final String SERVER_STORE_PWD = "server_ks_password"; protected static final String SERVER_STORE_PWD = "server_ks_password";
protected static final String SERVER_CERT_ALIAS = "server"; protected static final String SERVER_CERT_ALIAS = "server";
protected static final String SERVER_CERT_ALIAS_BS = "bootstrap"; protected static final String SERVER_CERT_ALIAS_BS = "bootstrap";
protected static final Security SECURITY_NO_SEC_BS = noSecBootstrap(URI_BS);;
protected final X509Certificate serverX509Cert; // server certificate signed by rootCA protected final X509Certificate serverX509Cert; // server certificate signed by rootCA
protected final X509Certificate serverX509CertBs; // serverBs certificate signed by rootCA protected final X509Certificate serverX509CertBs; // serverBs certificate signed by rootCA
protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK
@ -171,7 +172,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
LwM2MClientState finishState) throws Exception { LwM2MClientState finishState) throws Exception {
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(type)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(type));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint));
this.basicTestConnection(noSecBootstap(URI_BS), this.basicTestConnection(null , SECURITY_NO_SEC_BS,
deviceCredentials, deviceCredentials,
COAP_CONFIG_BS, COAP_CONFIG_BS,
clientEndpoint, clientEndpoint,
@ -183,22 +184,24 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
false); false);
} }
protected void basicTestConnection(Security security, protected void basicTestConnection(Security security, Security securityBs,
LwM2MDeviceCredentials deviceCredentials, LwM2MDeviceCredentials deviceCredentials,
Configuration coapConfig, Configuration coapConfig,
String endpoint, String endpoint,
Lwm2mDeviceProfileTransportConfiguration transportConfiguration, Lwm2mDeviceProfileTransportConfiguration transportConfiguration,
String awaitAlias, String awaitAlias,
Set<LwM2MClientState> expectedStatuses, Set<LwM2MClientState> expectedStatuses,
boolean isBootstrap, boolean isAwaitObserveReadAll,
LwM2MClientState finishState, LwM2MClientState finishState,
boolean isStartLw) throws Exception { boolean isStartLw) throws Exception {
createDeviceProfile(transportConfiguration); createDeviceProfile(transportConfiguration);
final Device device = createDevice(deviceCredentials, endpoint); final Device device = createDevice(deviceCredentials, endpoint);
device.getId().getId().toString(); device.getId().getId().toString();
createNewClient(security, coapConfig, true, endpoint, isBootstrap, null); createNewClient(security, securityBs, coapConfig, true, endpoint);
lwM2MTestClient.start(isStartLw); lwM2MTestClient.start(isStartLw);
awaitObserveReadAll(0, isBootstrap, device.getId().getId().toString()); if (isAwaitObserveReadAll) {
awaitObserveReadAll(0, device.getId().getId().toString());
}
await(awaitAlias) await(awaitAlias)
.atMost(40, TimeUnit.SECONDS) .atMost(40, TimeUnit.SECONDS)
.until(() -> { .until(() -> {
@ -220,34 +223,31 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint));
this.basicTestConnectionBootstrapRequestTrigger( this.basicTestConnectionBootstrapRequestTrigger(
SECURITY_NO_SEC, SECURITY_NO_SEC,
SECURITY_NO_SEC_BS,
deviceCredentials, deviceCredentials,
COAP_CONFIG, COAP_CONFIG,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
awaitAlias, awaitAlias,
expectedStatusesRegistrationLwm2mSuccess, expectedStatusesRegistrationLwm2mSuccess,
expectedStatusesRegistrationBsSuccess, expectedStatusesRegistrationBsSuccess);
false,
SECURITY_NO_SEC_BS);
} }
private void basicTestConnectionBootstrapRequestTrigger(Security security, private void basicTestConnectionBootstrapRequestTrigger(Security security, Security securityBs,
LwM2MDeviceCredentials deviceCredentials, LwM2MDeviceCredentials deviceCredentials,
Configuration coapConfig, Configuration coapConfig,
String endpoint, String endpoint,
Lwm2mDeviceProfileTransportConfiguration transportConfiguration, Lwm2mDeviceProfileTransportConfiguration transportConfiguration,
String awaitAlias, String awaitAlias,
Set<LwM2MClientState> expectedStatusesLwm2m, Set<LwM2MClientState> expectedStatusesLwm2m,
Set<LwM2MClientState> expectedStatusesBs, Set<LwM2MClientState> expectedStatusesBs) throws Exception {
boolean isBootstrap,
Security securityBs) throws Exception {
createDeviceProfile(transportConfiguration); createDeviceProfile(transportConfiguration);
final Device device = createDevice(deviceCredentials, endpoint); final Device device = createDevice(deviceCredentials, endpoint);
String deviceIdStr = device.getId().getId().toString(); String deviceIdStr = device.getId().getId().toString();
createNewClient(security, coapConfig, true, endpoint, isBootstrap, securityBs); createNewClient(security, securityBs, coapConfig, true, endpoint);
lwM2MTestClient.start(true); lwM2MTestClient.start(true);
awaitObserveReadAll(0, isBootstrap, deviceIdStr); awaitObserveReadAll(0, deviceIdStr);
await(awaitAlias) await(awaitAlias)
.atMost(40, TimeUnit.SECONDS) .atMost(40, TimeUnit.SECONDS)
.until(() -> { .until(() -> {
@ -263,7 +263,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatusesLwm2m)); Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatusesLwm2m));
String executedPath = "/" + OBJECT_ID_1 + "_" + lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_1).version String executedPath = "/" + OBJECT_ID_1 + "_" + lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_1).version
+ "/0/" + RESOURCE_ID_9; + "/" +serverId + "/" + RESOURCE_ID_9;
lwM2MTestClient.setClientStates(new HashSet<>()); lwM2MTestClient.setClientStates(new HashSet<>());
String actualResult = sendRPCSecurityExecuteById(executedPath, deviceIdStr, endpoint); String actualResult = sendRPCSecurityExecuteById(executedPath, deviceIdStr, endpoint);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -337,7 +337,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
default: default:
throw new IllegalStateException("Unexpected value: " + mode); throw new IllegalStateException("Unexpected value: " + mode);
} }
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs : shortServerId); bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort); bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort);
@ -443,3 +443,4 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk());
} }
} }

18
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java

@ -19,7 +19,6 @@ import org.junit.Test;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials;
import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_SUCCESS;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH;
@ -39,7 +38,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT
// Bootstrap + Lwm2m // Bootstrap + Lwm2m
@Test @Test
public void testWithNoSecConnectBsSuccess_UpdateTwoSectionsBootstrapAndLm2m_ConnectLwm2mSuccess() throws Exception { public void testWithNoSecConnectBsSuccess_UpdateTwoSectionsBootstrapAndLm2m_ConnectLwm2mSuccess() throws Exception {
String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS; String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + BOTH.name();
String awaitAlias = "await on client state (NoSecBS two section)"; String awaitAlias = "await on client state (NoSecBS two section)";
basicTestConnectionBefore(clientEndpoint, awaitAlias, BOTH, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS); basicTestConnectionBefore(clientEndpoint, awaitAlias, BOTH, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS);
} }
@ -51,20 +50,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT
basicTestConnectionBefore(clientEndpoint, awaitAlias, LWM2M_ONLY, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS); basicTestConnectionBefore(clientEndpoint, awaitAlias, LWM2M_ONLY, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS);
} }
@Test // Bs trigger
public void testWithNoSecConnectBsSuccess_UpdateBootstrapSectionAndLm2m_ConnectLwm2mSuccess() throws Exception {
String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + BOOTSTRAP_ONLY.name();
String awaitAlias = "await on client state (NoSecBS Bootstrap section)";
basicTestConnectionBefore(clientEndpoint, awaitAlias, BOOTSTRAP_ONLY, expectedStatusesBsSuccess, ON_BOOTSTRAP_SUCCESS);
}
@Test
public void testWithNoSecConnectBsSuccess_UpdateNoneSectionAndLm2m_ConnectLwm2mSuccess() throws Exception {
String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + NONE.name();
String awaitAlias = "await on client state (NoSecBS None section)";
basicTestConnectionBefore(clientEndpoint, awaitAlias, NONE, expectedStatusesBsSuccess, ON_BOOTSTRAP_SUCCESS);
}
@Test @Test
public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateTwoSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateTwoSectionAndLm2m_ConnectLwm2mSuccess() throws Exception {
String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + BOTH.name(); String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + BOTH.name();

9
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java

@ -55,13 +55,14 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false);
this.basicTestConnection(security, this.basicTestConnection(security,
null,
deviceCredentials, deviceCredentials,
COAP_CONFIG, COAP_CONFIG,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (Psk_Lwm2m)", "await on client state (Psk_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess, expectedStatusesRegistrationLwm2mSuccess,
false, true,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }
@ -100,15 +101,15 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Hex.decodeHex(keyPsk.toCharArray())); Hex.decodeHex(keyPsk.toCharArray()));
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, BOTH)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false);
this.basicTestConnection(securityBs, this.basicTestConnection(null, securityBs,
deviceCredentials, deviceCredentials,
COAP_CONFIG_BS, COAP_CONFIG_BS,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (PskBS two section)", "await on client state (PskBS two section)",
expectedStatusesRegistrationBsSuccess, expectedStatusesRegistrationBsSuccess,
true, false,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); false);
} }
} }

10
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java

@ -49,21 +49,21 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
RPKClientCredential clientCredentials = new RPKClientCredential(); RPKClientCredential clientCredentials = new RPKClientCredential();
clientCredentials.setEndpoint(clientEndpoint); clientCredentials.setEndpoint(clientEndpoint);
clientCredentials.setKey(Base64.encodeBase64String(certificate.getPublicKey().getEncoded())); clientCredentials.setKey(Base64.encodeBase64String(certificate.getPublicKey().getEncoded()));
Security securityBs = rpk(SECURE_URI, Security security = rpk(SECURE_URI,
shortServerId, shortServerId,
certificate.getPublicKey().getEncoded(), certificate.getPublicKey().getEncoded(),
privateKey.getEncoded(), privateKey.getEncoded(),
serverX509Cert.getPublicKey().getEncoded()); serverX509Cert.getPublicKey().getEncoded());
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, NONE)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, RPK, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, RPK, false);
this.basicTestConnection(securityBs, this.basicTestConnection(security, null,
deviceCredentials, deviceCredentials,
COAP_CONFIG, COAP_CONFIG,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (Rpk_Lwm2m)", "await on client state (Rpk_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess, expectedStatusesRegistrationLwm2mSuccess,
false, true,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }
@ -117,14 +117,14 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
serverX509CertBs.getPublicKey().getEncoded()); serverX509CertBs.getPublicKey().getEncoded());
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, BOTH)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, clientPrivateKeyFromCertTrust, certificate, RPK, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, clientPrivateKeyFromCertTrust, certificate, RPK, false);
this.basicTestConnection(securityBs, this.basicTestConnection(null, securityBs,
deviceCredentials, deviceCredentials,
COAP_CONFIG_BS, COAP_CONFIG_BS,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (RpkBS two section)", "await on client state (RpkBS two section)",
expectedStatusesRegistrationBsSuccess, expectedStatusesRegistrationBsSuccess,
true, false,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }

6
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java

@ -57,13 +57,14 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, NONE)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security, this.basicTestConnection(security,
null,
deviceCredentials, deviceCredentials,
COAP_CONFIG, COAP_CONFIG,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (X509_Trust_Lwm2m)", "await on client state (X509_Trust_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess, expectedStatusesRegistrationLwm2mSuccess,
false, true,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }
@ -118,13 +119,14 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security, this.basicTestConnection(security,
null,
deviceCredentials, deviceCredentials,
COAP_CONFIG_BS, COAP_CONFIG_BS,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (X509NoTrust two section)", "await on client state (X509NoTrust two section)",
expectedStatusesRegistrationBsSuccess, expectedStatusesRegistrationBsSuccess,
true, false,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }

6
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java

@ -51,13 +51,14 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, NONE)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security, this.basicTestConnection(security,
null,
deviceCredentials, deviceCredentials,
COAP_CONFIG, COAP_CONFIG,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (X509_Trust_Lwm2m)", "await on client state (X509_Trust_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess, expectedStatusesRegistrationLwm2mSuccess,
false, true,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }
@ -78,13 +79,14 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security, this.basicTestConnection(security,
null,
deviceCredentials, deviceCredentials,
COAP_CONFIG_BS, COAP_CONFIG_BS,
clientEndpoint, clientEndpoint,
transportConfiguration, transportConfiguration,
"await on client state (X509Trust two section)", "await on client state (X509Trust two section)",
expectedStatusesRegistrationBsSuccess, expectedStatusesRegistrationBsSuccess,
true, false,
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }

2
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java

@ -26,7 +26,7 @@ public class LwM2MServerSecurityConfig implements Serializable {
@Schema(description = "Server short Id. Used as link to associate server Object Instance. This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client. " + @Schema(description = "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'. " + "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", accessMode = Schema.AccessMode.READ_ONLY) "The values ID:1 and ID:65534 values MUST NOT be used for identifying the LwM2M Server.", example = "123", accessMode = Schema.AccessMode.READ_ONLY)
protected Integer shortServerId = 123; protected Integer shortServerId = 123;
/** Security -> ObjectId = 0 'LWM2M Security' */ /** Security -> ObjectId = 0 'LWM2M Security' */
@Schema(description = "Is Bootstrap Server or Lwm2m Server. " + @Schema(description = "Is Bootstrap Server or Lwm2m Server. " +

6
common/data/src/main/java/org/thingsboard/server/common/data/util/TbDDFFileParser.java

@ -17,8 +17,6 @@ package org.thingsboard.server.common.data.util;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.LwM2m; import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.model.DDFFileValidator;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
@ -41,7 +39,7 @@ import java.util.Map;
@Slf4j @Slf4j
public class TbDDFFileParser { public class TbDDFFileParser {
private static final DDFFileValidator ddfFileValidator = new DefaultDDFFileValidator(); private static final TbDefaultDDFFileValidator ddfFileValidator = new TbDefaultDDFFileValidator();
public List<ObjectModel> parse(InputStream inputStream, String streamName) public List<ObjectModel> parse(InputStream inputStream, String streamName)
throws InvalidDDFFileException, IOException { throws InvalidDDFFileException, IOException {
@ -269,4 +267,4 @@ public class TbDDFFileParser {
} }
return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description); return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description);
} }
} }

129
common/data/src/main/java/org/thingsboard/server/common/data/util/TbDefaultDDFFileValidator.java

@ -0,0 +1,129 @@
/**
* Copyright © 2016-2023 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.util;
import org.eclipse.leshan.core.LwM2m.LwM2mVersion;
import org.eclipse.leshan.core.model.DDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.util.Validate;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.IOException;
import java.io.InputStream;
/**
* A DDF File Validator.
* <p>
* Validate a DDF File against the embedded LWM2M schema.
* <p>
* Support LWM2M version 1.0 and 1.1.
*/
public class TbDefaultDDFFileValidator implements DDFFileValidator {
private static String LWM2M_V1_0_SCHEMA_PATH = "/schemas/LWM2M.xsd";
private static String LWM2M_V1_1_SCHEMA_PATH = "/schemas/LWM2M-v1_1.xsd";
private final String schema;
/**
* Create a {@link DDFFileValidator} using the LWM2M v1.1 schema.
*/
public TbDefaultDDFFileValidator() {
this(LwM2mVersion.V1_1);
}
/**
* Create a {@link DDFFileValidator} using schema corresponding to LWM2M {@link LwM2mVersion}.
*/
public TbDefaultDDFFileValidator(LwM2mVersion version) {
Validate.notNull(version, "version must not be null");
if (LwM2mVersion.V1_0.equals(version)) {
schema = LWM2M_V1_0_SCHEMA_PATH;
} else if (LwM2mVersion.V1_1.equals(version)) {
schema = LWM2M_V1_1_SCHEMA_PATH;
} else {
throw new IllegalStateException(String.format("Unsupported version %s", version));
}
}
@Override
public void validate(Node xmlToValidate) throws InvalidDDFFileException {
try {
validate(new DOMSource(xmlToValidate));
} catch (SAXException | IOException e) {
throw new InvalidDDFFileException(e);
}
}
/**
* Validate a XML {@link Source} against the embedded LWM2M Schema.
*
* @param xmlToValidate an XML source to validate
* @throws SAXException see {@link Validator#validate(Source)}
* @throws IOException see {@link Validator#validate(Source)}
*/
public void validate(Source xmlToValidate) throws SAXException, IOException {
Validator validator = getEmbeddedLwM2mSchema().newValidator();
validator.validate(xmlToValidate);
}
/**
* Get the Embedded the LWM2M.xsd Schema.
*
* @throws SAXException see {@link SchemaFactory#newSchema(Source)}
*/
protected Schema getEmbeddedLwM2mSchema() throws SAXException {
InputStream inputStream = DDFFileValidator.class.getResourceAsStream(schema);
Source source = new StreamSource(inputStream);
SchemaFactory schemaFactory = createSchemaFactory();
return schemaFactory.newSchema(source);
}
protected SchemaFactory createSchemaFactory() {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
try {
// Create Safe SchemaFactory (not vulnerable to XXE Attacks)
// --------------------------------------------------------
// There is several recommendation from different source we try to apply all, even if some are maybe
// redundant.
// from :
// https://semgrep.dev/docs/cheat-sheets/java-xxe/
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// from :
// https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#schemafactory
// factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
// factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
throw new IllegalStateException("Unable to create SchemaFactory", e);
}
return factory;
}
}

149
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

@ -15,13 +15,20 @@
*/ */
package org.thingsboard.server.transport.lwm2m.bootstrap; package org.thingsboard.server.transport.lwm2m.bootstrap;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.scandium.config.DtlsConfig; import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.core.endpoint.Protocol;
import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; import org.eclipse.leshan.server.bootstrap.LeshanBootstrapServer;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder; import org.eclipse.leshan.server.bootstrap.LeshanBootstrapServerBuilder;
import org.eclipse.leshan.server.californium.bootstrap.LwM2mBootstrapPskStore;
import org.eclipse.leshan.server.californium.bootstrap.endpoint.CaliforniumBootstrapServerEndpointsProvider;
import org.eclipse.leshan.server.californium.bootstrap.endpoint.coap.CoapBootstrapServerProtocolProvider;
import org.eclipse.leshan.server.californium.bootstrap.endpoint.coaps.CoapsBootstrapServerProtocolProvider;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials; import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
@ -33,16 +40,12 @@ import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBoots
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import jakarta.annotation.PostConstruct; import java.net.InetSocketAddress;
import jakarta.annotation.PreDestroy;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
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_RECOMMENDED_CURVES_ONLY;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE;
import static org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole.SERVER_ONLY;
import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.PSK_CIPHER_SUITES; import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.PSK_CIPHER_SUITES;
import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES; import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig; import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig;
@ -82,24 +85,121 @@ public class LwM2MTransportBootstrapService {
public LeshanBootstrapServer getLhBootstrapServer() { public LeshanBootstrapServer getLhBootstrapServer() {
LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder(); LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
builder.setLocalAddress(bootstrapConfig.getHost(), bootstrapConfig.getPort());
builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort());
/* Create CoAP Config */ // Create Californium Endpoints Provider:
builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig)); // ------------------
// Create Server Endpoints Provider
CaliforniumBootstrapServerEndpointsProvider.Builder endpointsBuilder = new CaliforniumBootstrapServerEndpointsProvider.Builder(
// Add coap Protocol support
new CoapBootstrapServerProtocolProvider(),
// Add coaps/dtls protocol support
new CoapsBootstrapServerProtocolProvider(c -> {
if (this.bootstrapConfig.getSslCredentials() != null) {
c.setAdvancedCertificateVerifier(certificateVerifier);
c.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else {
log.info("Unable to load X509 files for LWM2MServer");
LwM2mBootstrapPskStore lwM2mBsPskStore = new LwM2mBootstrapPskStore(lwM2MBootstrapSecurityStore);
c.setAdvancedPskStore(lwM2mBsPskStore);
c.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, PSK_CIPHER_SUITES);
}
}));
// Create Californium Configuration
Configuration serverCoapConfig = endpointsBuilder.createDefaultConfiguration();
getCoapConfig(serverCoapConfig, bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(),serverConfig);
serverCoapConfig.setTransient(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY);
serverCoapConfig.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, serverConfig.isRecommendedCiphers());
serverCoapConfig.setTransient(DtlsConfig.DTLS_CONNECTION_ID_LENGTH);
int cid = 6;
if (cid > 0) {
serverCoapConfig.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, cid);
}
serverCoapConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, serverConfig.isRecommendedSupportedGroups());
serverCoapConfig.setTransient(DTLS_RETRANSMISSION_TIMEOUT);
serverCoapConfig.set(DTLS_RETRANSMISSION_TIMEOUT, serverConfig.getDtlsRetransmissionTimeout(), MILLISECONDS);
/* Create DTLS Config */
this.setServerWithCredentials(builder);
// DtlsConnectorConfig dtlsConfig;
// try {
// dtlsConfig = dtlsConfigBuilder.build();
// } catch (IllegalStateException e) {
// log.warn("Unable to create DTLS config for endpont {}.", endpointUri.toString(), e);
// return null;
// }
//
// Connector dTLSConnector = new DTLSConnector(dtlsConfig);
/* Create and Set DTLS Config */ // endpointsBuilder.setConnector(dTLSConnector);
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig)); // endpointsBuilder.setConfiguration(serverCoapConfig);
dtlsConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, serverConfig.isRecommendedSupportedGroups()); // endpointsBuilder.setLoggingTag(String.format("[%s]", "/" + "options.getUriPathString()"));
dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, serverConfig.isRecommendedCiphers()); // endpointsBuilder.setEndpointContextMatcher(new Lwm2mEndpointContextMatcher());
dtlsConfig.set(DTLS_RETRANSMISSION_TIMEOUT, serverConfig.getDtlsRetransmissionTimeout(), MILLISECONDS);
dtlsConfig.set(DTLS_ROLE, SERVER_ONLY);
setServerWithCredentials(builder, dtlsConfig);
/* Set DTLS Config */ /* Create credentials */
builder.setDtlsConfig(dtlsConfig);
// Set Californium Configuration
endpointsBuilder.setConfiguration(serverCoapConfig);
// Create CoAP endpoint
InetSocketAddress coapAddr = new InetSocketAddress(bootstrapConfig.getHost(), bootstrapConfig.getPort());
endpointsBuilder.addEndpoint(coapAddr, Protocol.COAP);
// Create CoAP over DTLS endpoint
InetSocketAddress coapsAddr = new InetSocketAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort());
endpointsBuilder.addEndpoint(coapsAddr, Protocol.COAPS);
// builder.setLocalAddress(config.getHost(), config.getPort());
// builder.setLocalSecureAddress(config.getSecureHost(), config.getSecurePort());
// builder.setDecoder(new DefaultLwM2mDecoder());
/* Use a magic converter to support bad type send by the UI. */
// builder.setEncoder(new DefaultLwM2mEncoder(LwM2mValueConverterImpl.getInstance()));
/* Create CoAP Config */
// builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort(), config));
//
// /* Set securityStore with new registrationStore */
// builder.setSecurityStore(securityStore);
// builder.setRegistrationStore(registrationStore);
//
//
// // Create LWM2M server
// builder.setEndpointsProviders(endpointsBuilder.build());
//
//
//
// builder.setLocalAddress(bootstrapConfig.getHost(), bootstrapConfig.getPort());
// builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort());
//
// /* Create CoAP Config */
// builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
//
//
// /* Create and Set DTLS Config */
// DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
//
// 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_ROLE, SERVER_ONLY);
// setServerWithCredentials(builder, dtlsConfig);
//
// /* Set DTLS Config */
// builder.setDtlsConfig(dtlsConfig);
/* Set securityStore with new ConfigStore */ /* Set securityStore with new ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
@ -112,22 +212,19 @@ public class LwM2MTransportBootstrapService {
builder.setSessionManager(sessionManager); builder.setSessionManager(sessionManager);
/* Create BootstrapServer */ /* Create BootstrapServer */
builder.setEndpointsProviders(endpointsBuilder.build());
return builder.build(); return builder.build();
} }
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) { private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
if (this.bootstrapConfig.getSslCredentials() != null) { if (this.bootstrapConfig.getSslCredentials() != null) {
SslCredentials sslCredentials = this.bootstrapConfig.getSslCredentials(); SslCredentials sslCredentials = this.bootstrapConfig.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey()); builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey()); builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain()); builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else { } else {
/* by default trust all */ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]); builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for BootStrap Server");
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, PSK_CIPHER_SUITES);
} }
} }
} }

35
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java

@ -16,10 +16,13 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.secure; package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.peer.IpPeer;
import org.eclipse.leshan.core.peer.LwM2mPeer;
import org.eclipse.leshan.core.peer.PskIdentity;
import org.eclipse.leshan.core.peer.X509Identity;
import org.eclipse.leshan.core.request.BootstrapDownlinkRequest; import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
import org.eclipse.leshan.core.request.BootstrapFinishRequest; import org.eclipse.leshan.core.request.BootstrapFinishRequest;
import org.eclipse.leshan.core.request.BootstrapRequest; import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore; import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause; import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause;
@ -39,6 +42,7 @@ import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecu
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapTaskProvider; import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapTaskProvider;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
@ -63,7 +67,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
* @param bsSecurityStore the {@link BootstrapSecurityStore} used by default {@link SecurityChecker}. * @param bsSecurityStore the {@link BootstrapSecurityStore} used by default {@link SecurityChecker}.
*/ */
public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore, TransportService transportService) { public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore, TransportService transportService) {
this(bsSecurityStore, new SecurityChecker(), new LwM2MBootstrapConfigStoreTaskProvider(configStore), this(bsSecurityStore, configStore, new SecurityChecker(), new LwM2MBootstrapConfigStoreTaskProvider(configStore),
new StandardBootstrapModelProvider()); new StandardBootstrapModelProvider());
this.transportService = transportService; this.transportService = transportService;
} }
@ -74,9 +78,9 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
* @param bsSecurityStore the {@link BootstrapSecurityStore} used by {@link SecurityChecker}. * @param bsSecurityStore the {@link BootstrapSecurityStore} used by {@link SecurityChecker}.
* @param securityChecker used to accept or refuse new {@link BootstrapSession}. * @param securityChecker used to accept or refuse new {@link BootstrapSession}.
*/ */
public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, SecurityChecker securityChecker, public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore, SecurityChecker securityChecker,
LwM2MBootstrapTaskProvider tasksProvider, LwM2mBootstrapModelProvider modelProvider) { LwM2MBootstrapTaskProvider tasksProvider, LwM2mBootstrapModelProvider modelProvider) {
super(bsSecurityStore, securityChecker, tasksProvider, modelProvider); super(bsSecurityStore, configStore);
this.bsSecurityStore = bsSecurityStore; this.bsSecurityStore = bsSecurityStore;
this.securityChecker = securityChecker; this.securityChecker = securityChecker;
this.tasksProvider = tasksProvider; this.tasksProvider = tasksProvider;
@ -84,23 +88,24 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
} }
@Override @Override
public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) { public BootstrapSession begin(BootstrapRequest request, LwM2mPeer sender, URI endpointUsed) {
boolean authorized = true; boolean authorized = sender.getIdentity().isSecure();
Iterator<SecurityInfo> securityInfos = null; Iterator<SecurityInfo> securityInfos = null;
try { try {
if (bsSecurityStore != null && securityChecker != null) { if (bsSecurityStore != null && securityChecker != null) {
if (clientIdentity.isPSK()) { if (((IpPeer) sender).isPSK()) {
SecurityInfo securityInfo = bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity()); SecurityInfo securityInfo = bsSecurityStore.getByIdentity(((PskIdentity) sender.getIdentity()).getPskIdentity());
securityInfos = Collections.singletonList(securityInfo).iterator(); securityInfos = Collections.singletonList(securityInfo).iterator();
} else if (!clientIdentity.isX509()) { }
else if (!((IpPeer) sender).isX509()) {
securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName()); securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
} }
authorized = this.checkSecurityInfo(request.getEndpointName(), clientIdentity, securityInfos); authorized = this.checkSecurityInfo(request.getEndpointName(), sender, securityInfos);
} }
} catch (LwM2MAuthException e) { } catch (LwM2MAuthException e) {
authorized = false; authorized = false;
} }
DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized); DefaultBootstrapSession session = new DefaultBootstrapSession(request, sender, authorized, null, endpointUsed);
if (authorized) { if (authorized) {
try { try {
this.tasksProvider.put(session.getEndpoint()); this.tasksProvider.put(session.getEndpoint());
@ -239,9 +244,9 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
transportService.log(((LwM2MBootstrapSecurityStore) bsSecurityStore).getSessionByEndpoint(endpointName), logMsg); transportService.log(((LwM2MBootstrapSecurityStore) bsSecurityStore).getSessionByEndpoint(endpointName), logMsg);
} }
private boolean checkSecurityInfo(String endpoint, Identity clientIdentity, Iterator<SecurityInfo> securityInfos) { private boolean checkSecurityInfo(String endpoint, LwM2mPeer clientIdentity, Iterator<SecurityInfo> securityInfos) {
if (clientIdentity.isX509()) { if (((IpPeer) clientIdentity).isX509()) {
return clientIdentity.getX509CommonName().equals(endpoint) return ((X509Identity)clientIdentity.getIdentity()).getX509CommonName().equals(endpoint)
& ((LwM2MBootstrapSecurityStore) bsSecurityStore).getBootstrapConfigByEndpoint(endpoint) != null; & ((LwM2MBootstrapSecurityStore) bsSecurityStore).getBootstrapConfigByEndpoint(endpoint) != null;
} else { } else {
return securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos); return securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos);

13
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java

@ -76,7 +76,8 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask
@Override @Override
public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResponse) { public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResponse) {
BootstrapConfig config = store.get(session.getEndpoint(), session.getIdentity(), session); // BootstrapConfig config = store.get(session.getEndpoint(), session.getClientTransportData().getIdentity(), session);
BootstrapConfig config = store.get(session);
if (config == null) { if (config == null) {
return null; return null;
} }
@ -150,7 +151,7 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask
* The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server. * The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.
* "Short Server ID": * "Short Server ID":
* - Link Instance (lwm2m Server) hase linkParams with key = "ssid" value = "shortId" (ver lvm2m = 1.1). * - Link Instance (lwm2m Server) hase linkParams with key = "ssid" value = "shortId" (ver lvm2m = 1.1).
* - Link Instance (bootstrap Server) hase not linkParams with key = "ssid" (ver lvm2m = 1.1). * - Link Instance (bootstrap Server) hase not linkParams with key = "ssid" (ver lvm2m = 1.0).
*/ */
protected void findSecurityInstanceId(Link[] objectLinks, String endpoint) { protected void findSecurityInstanceId(Link[] objectLinks, String endpoint) {
log.info("Object after discover: [{}]", objectLinks); log.info("Object after discover: [{}]", objectLinks);
@ -159,14 +160,14 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask
try { try {
LwM2mPath path = new LwM2mPath(link.getUriReference()); LwM2mPath path = new LwM2mPath(link.getUriReference());
if (path.isObjectInstance()) { if (path.isObjectInstance()) {
if (link.getLinkParams().containsKey("ssid")) { if (link.getAttributes().get("ssid") != null) {
int serverId = Integer.parseInt(link.getLinkParams().get("ssid").getUnquoted()); int serverId = Integer.parseInt(link.getAttributes().get("ssid").getCoreLinkValue());
if (!lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(serverId)) { if (!lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(serverId)) {
lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(serverId, path.getObjectInstanceId()); lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(serverId, path.getObjectInstanceId());
} else { } else {
log.error("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId()); log.error("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId());
} }
lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(Integer.valueOf(link.getLinkParams().get("ssid").getUnquoted()), path.getObjectInstanceId()); lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(serverId, path.getObjectInstanceId());
} else { } else {
if (!this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(0)) { if (!this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(0)) {
this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(BOOTSTRAP_DEFAULT_SHORT_ID, path.getObjectInstanceId()); this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(BOOTSTRAP_DEFAULT_SHORT_ID, path.getObjectInstanceId());
@ -221,7 +222,7 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask
LwM2mPath path = new LwM2mPath(link.getUriReference()); LwM2mPath path = new LwM2mPath(link.getUriReference());
if (!path.isRoot() && path.getObjectId() < 3) { if (!path.isRoot() && path.getObjectId() < 3) {
if (path.isObject()) { if (path.isObject()) {
String ver = link.getLinkParams().get("ver") != null ? link.getLinkParams().get("ver").getUnquoted() : "1.0"; String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver").getCoreLinkValue() : "1.0";
this.supportedObjects.put(path.getObjectId(), ver); this.supportedObjects.put(path.getObjectId(), ver);
} }
} }

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.store; package org.thingsboard.server.transport.lwm2m.bootstrap.store;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.peer.OscoreIdentity;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig; import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore; import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
@ -98,6 +99,11 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
} }
@Override
public SecurityInfo getByOscoreIdentity(OscoreIdentity oscoreIdentity) {
return null;
}
public TbLwM2MSecurityInfo getX509ByEndpoint(String endPoint) { public TbLwM2MSecurityInfo getX509ByEndpoint(String endPoint) {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
this.addValueToStore(store, store.getEndpoint()); this.addValueToStore(store, store.getEndpoint());
@ -210,4 +216,4 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
return securityInfo; return securityInfo;
} }
} }

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MConfigurationChecker.java

@ -58,17 +58,25 @@ public class LwM2MConfigurationChecker extends ConfigurationChecker {
for (Map.Entry<Integer, BootstrapConfig.ServerConfig> e : config.servers.entrySet()) { for (Map.Entry<Integer, BootstrapConfig.ServerConfig> e : config.servers.entrySet()) {
BootstrapConfig.ServerConfig srvCfg = e.getValue(); BootstrapConfig.ServerConfig srvCfg = e.getValue();
// shortId checks
if (srvCfg.shortId == 0) {
throw new InvalidConfigurationException("short ID must not be 0");
}
// look for security entry // look for security entry
BootstrapConfig.ServerSecurity security = getSecurityEntry(config, srvCfg.shortId); BootstrapConfig.ServerSecurity security = getSecurityEntry(config, srvCfg.shortId);
if (security == null) { if (security == null) {
throw new InvalidConfigurationException("no security entry for server instance: " + e.getKey()); throw new InvalidConfigurationException("no security entry for server instance: " + e.getKey());
} }
// BS Server
if (security.bootstrapServer && srvCfg.shortId != 0) {
throw new InvalidConfigurationException("short ID must be 0");
}
// LwM2M Server
/**
* This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client.
* This Resource MUST be set when the Bootstrap-Server Resource has false value.
* Specific ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server (Section 6.3 of the LwM2M version 1.0 specification).
*/
if (!security.bootstrapServer && (srvCfg.shortId < 1 && srvCfg.shortId > 65534 )) {
throw new InvalidConfigurationException("Specific ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server");
}
} }
} }
@ -81,4 +89,4 @@ public class LwM2MConfigurationChecker extends ConfigurationChecker {
return null; return null;
} }
} }

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java

@ -16,9 +16,7 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.store; package org.thingsboard.server.transport.lwm2m.bootstrap.store;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig; import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.ConfigurationChecker; import org.eclipse.leshan.server.bootstrap.ConfigurationChecker;
import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore; import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
@ -39,8 +37,8 @@ public class LwM2MInMemoryBootstrapConfigStore extends InMemoryBootstrapConfigSt
private final Lock writeLock = readWriteLock.writeLock(); private final Lock writeLock = readWriteLock.writeLock();
protected final ConfigurationChecker configChecker = new LwM2MConfigurationChecker(); protected final ConfigurationChecker configChecker = new LwM2MConfigurationChecker();
@Override
public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) { public BootstrapConfig get(String endpoint) {
return bootstrapByEndpoint.get(endpoint); return bootstrapByEndpoint.get(endpoint);
} }

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java

@ -67,14 +67,14 @@ public class LwM2mCredentialsSecurityInfoValidator {
new TransportServiceCallback<>() { new TransportServiceCallback<>() {
@Override @Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) { public void onSuccess(ValidateDeviceCredentialsResponse msg) {
log.trace("Validated credentials: [{}] [{}]", credentialsId, msg); log.info("Validated credentials: [{}] [{}]", credentialsId, msg);
resultSecurityStore[0] = createSecurityInfo(credentialsId, msg, keyValue); resultSecurityStore[0] = createSecurityInfo(credentialsId, msg, keyValue);
latch.countDown(); latch.countDown();
} }
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e); log.info("[{}] [{}] Failed to process credentials ", credentialsId, e);
TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo(); TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
result.setEndpoint(credentialsId); result.setEndpoint(credentialsId);
resultSecurityStore[0] = result; resultSecurityStore[0] = result;

38
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MAuthorizer.java

@ -18,9 +18,11 @@ package org.thingsboard.server.transport.lwm2m.secure;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.peer.LwM2mPeer;
import org.eclipse.leshan.core.peer.X509Identity;
import org.eclipse.leshan.core.request.UplinkRequest; import org.eclipse.leshan.core.request.UplinkRequest;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.Authorization;
import org.eclipse.leshan.server.security.Authorizer; import org.eclipse.leshan.server.security.Authorizer;
import org.eclipse.leshan.server.security.SecurityChecker; import org.eclipse.leshan.server.security.SecurityChecker;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
@ -45,38 +47,30 @@ public class TbLwM2MAuthorizer implements Authorizer {
private final LwM2mClientContext clientContext; private final LwM2mClientContext clientContext;
@Override @Override
public Registration isAuthorized(UplinkRequest<?> request, Registration registration, Identity senderIdentity) { public Authorization isAuthorized(UplinkRequest<?> request, Registration registration, LwM2mPeer sender) {
if (senderIdentity.isX509()) { SecurityInfo expectedSecurityInfo = null;
TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint()); if (securityStore != null) expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
if (sessionInfo != null) { if (securityChecker.checkSecurityInfo(registration.getEndpoint(), sender, expectedSecurityInfo)) {
if (sessionInfo.getX509CommonName().endsWith(senderIdentity.getX509CommonName())) { if (sender.getIdentity() instanceof X509Identity) {
clientContext.registerClient(registration, sessionInfo.getCredentials()); TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint());
if (sessionInfo != null) {
// X509 certificate is valid and matches endpoint. // X509 certificate is valid and matches endpoint.
return registration; clientContext.registerClient(registration, sessionInfo.getCredentials());
} else {
// X509 certificate is not valid.
return null;
} }
} }
// If session info is not found, this may be the trusted certificate, so we still need to check all other options below.
}
SecurityInfo expectedSecurityInfo;
try { try {
expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
if (expectedSecurityInfo != null && expectedSecurityInfo.usePSK() && expectedSecurityInfo.getEndpoint().equals(SecurityMode.NO_SEC.toString()) if (expectedSecurityInfo != null && expectedSecurityInfo.usePSK() && expectedSecurityInfo.getEndpoint().equals(SecurityMode.NO_SEC.toString())
&& expectedSecurityInfo.getIdentity().equals(SecurityMode.NO_SEC.toString()) && expectedSecurityInfo.getPskIdentity().equals(SecurityMode.NO_SEC.toString())
&& Arrays.equals(SecurityMode.NO_SEC.toString().getBytes(), expectedSecurityInfo.getPreSharedKey())) { && Arrays.equals(SecurityMode.NO_SEC.toString().getBytes(), expectedSecurityInfo.getPreSharedKey())) {
expectedSecurityInfo = null; return Authorization.declined();
} }
} catch (LwM2MAuthException e) { } catch (LwM2MAuthException e) {
log.info("Registration failed: FORBIDDEN, endpointId: [{}]", registration.getEndpoint()); log.info("Registration failed: FORBIDDEN, endpointId: [{}]", registration.getEndpoint());
return null; return Authorization.declined();
} }
if (securityChecker.checkSecurityInfo(registration.getEndpoint(), senderIdentity, expectedSecurityInfo)) { return Authorization.approved();
return registration;
} else { } else {
securityStore.remove(registration.getEndpoint(), registration.getId()); return Authorization.declined();
return null;
} }
} }
} }

3
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java

@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.leshan.core.californium.LwM2mCoapResource; import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.eclipse.leshan.core.californium.identity.IdentityHandlerProvider;
@Slf4j @Slf4j
public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource { public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource {
public AbstractLwM2mTransportResource(String name) { public AbstractLwM2mTransportResource(String name) {
super(name); super(name, new IdentityHandlerProvider());
} }
@Override @Override

141
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java

@ -15,16 +15,22 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.scandium.config.DtlsConfig; import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
import org.eclipse.leshan.core.endpoint.Protocol;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder;
import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.LeshanServerBuilder;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.LwM2mPskStore;
import org.eclipse.leshan.server.californium.endpoint.CaliforniumServerEndpointsProvider;
import org.eclipse.leshan.server.californium.endpoint.coap.CoapServerProtocolProvider;
import org.eclipse.leshan.server.californium.endpoint.coaps.CoapsServerProtocolProvider;
import org.eclipse.leshan.server.registration.RegistrationStore;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.cache.ota.OtaPackageDataCache;
@ -37,13 +43,11 @@ import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore; import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import jakarta.annotation.PreDestroy; import java.net.InetSocketAddress;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
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_RECOMMENDED_CURVES_ONLY;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE;
@ -64,12 +68,13 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
public static final CipherSuite[] RPK_OR_X509_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}; public static final CipherSuite[] RPK_OR_X509_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256};
public static final CipherSuite[] PSK_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256}; public static final CipherSuite[] PSK_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256};
// public static final CipherSuite[] PSK_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8};
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
private final OtaPackageDataCache otaPackageDataCache; private final OtaPackageDataCache otaPackageDataCache;
private final LwM2mUplinkMsgHandler handler; private final LwM2mUplinkMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore; private final RegistrationStore registrationStore;
private final TbSecurityStore securityStore; private final TbSecurityStore securityStore;
private final TbLwM2MDtlsCertificateVerifier certificateVerifier; private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
private final TbLwM2MAuthorizer authorizer; private final TbLwM2MAuthorizer authorizer;
@ -88,7 +93,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
* "coap://host:port/{path}/{token}/{nameFile}" * "coap://host:port/{path}/{token}/{nameFile}"
*/ */
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE); LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
this.server.coap().getServer().add(otaCoapResource); // this.server.coap().getServer().add(otaCoapResource);
this.context.setServer(server); this.context.setServer(server);
this.startLhServer(); this.startLhServer();
} }
@ -117,54 +122,128 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private LeshanServer getLhServer() { private LeshanServer getLhServer() {
LeshanServerBuilder builder = new LeshanServerBuilder(); LeshanServerBuilder builder = new LeshanServerBuilder();
builder.setLocalAddress(config.getHost(), config.getPort());
builder.setLocalSecureAddress(config.getSecureHost(), config.getSecurePort());
builder.setDecoder(new DefaultLwM2mDecoder());
/* Use a magic converter to support bad type send by the UI. */
builder.setEncoder(new DefaultLwM2mEncoder(LwM2mValueConverterImpl.getInstance()));
/* Create CoAP Config */
builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort(), config));
/* Define model provider (Create Models )*/ /* Define model provider (Create Models )*/
builder.setObjectModelProvider(modelProvider); builder.setObjectModelProvider(modelProvider);
/* Set securityStore with new registrationStore */ /* Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore); builder.setSecurityStore(securityStore);
builder.setRegistrationStore(registrationStore); builder.setRegistrationStore(registrationStore);
// Create Californium Endpoints Provider:
// ------------------
// Create Server Endpoints Provider
CaliforniumServerEndpointsProvider.Builder endpointsBuilder = new CaliforniumServerEndpointsProvider.Builder(
// Add coap Protocol support
new CoapServerProtocolProvider(),
// Add coaps/dtls protocol support
new CoapsServerProtocolProvider(c -> {
if (this.config.getSslCredentials() != null) {
c.setAdvancedCertificateVerifier(certificateVerifier);
c.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else {
log.info("Unable to load X509 files for LWM2MServer");
LwM2mPskStore lwM2mPskStore = new LwM2mPskStore(securityStore, registrationStore);
c.setAdvancedPskStore(lwM2mPskStore);
c.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, PSK_CIPHER_SUITES);
}
}));
// Create Californium Configuration
Configuration serverCoapConfig = endpointsBuilder.createDefaultConfiguration();
getCoapConfig(serverCoapConfig, config.getPort(), config.getSecurePort(), config);
// Set some DTLS stuff
serverCoapConfig.setTransient(DTLS_RECOMMENDED_CURVES_ONLY);
serverCoapConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, config.isRecommendedSupportedGroups());
serverCoapConfig.setTransient(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY);
serverCoapConfig.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, config.isRecommendedCiphers());
serverCoapConfig.set(DTLS_RETRANSMISSION_TIMEOUT, config.getDtlsRetransmissionTimeout(), MILLISECONDS);
serverCoapConfig.set(DTLS_ROLE, SERVER_ONLY);
serverCoapConfig.setTransient(DtlsConfig.DTLS_CONNECTION_ID_LENGTH);
/**
* "Control usage of DTLS connection ID.", //
* "- 'on' to activate Connection ID support ", //
* " (same as -cid 6)", //
* "- 'off' to deactivate it", //
* "- Positive value define the size in byte of CID generated.", //
* "- 0 value means we accept to use CID but will not generated one for foreign peer.", //
* "Default: on"
*/
int cid = 6;
serverCoapConfig.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, cid);
/* Create DTLS Config */ /* Create DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(getCoapConfig(config.getPort(), config.getSecurePort(), config)); this.setServerWithCredentials(builder);
// DtlsConnectorConfig dtlsConfig;
// try {
// dtlsConfig = dtlsConfigBuilder.build();
// } catch (IllegalStateException e) {
// log.warn("Unable to create DTLS config for endpont {}.", endpointUri.toString(), e);
// return null;
// }
//
// Connector dTLSConnector = new DTLSConnector(dtlsConfig);
// endpointsBuilder.setConnector(dTLSConnector);
// endpointsBuilder.setConfiguration(serverCoapConfig);
dtlsConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, config.isRecommendedSupportedGroups()); // endpointsBuilder.setLoggingTag(String.format("[%s]", "/" + "options.getUriPathString()"));
dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, config.isRecommendedCiphers()); // endpointsBuilder.setEndpointContextMatcher(new Lwm2mEndpointContextMatcher());
dtlsConfig.set(DTLS_RETRANSMISSION_TIMEOUT, config.getDtlsRetransmissionTimeout(), MILLISECONDS);
dtlsConfig.set(DTLS_ROLE, SERVER_ONLY);
/* Create credentials */ /* Create credentials */
this.setServerWithCredentials(builder, dtlsConfig);
/* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
/* Create LWM2M server */
// Set Californium Configuration
endpointsBuilder.setConfiguration(serverCoapConfig);
// Create CoAP endpoint
InetSocketAddress coapAddr = new InetSocketAddress(config.getHost(), config.getPort());
endpointsBuilder.addEndpoint(coapAddr, Protocol.COAP);
// Create CoAP over DTLS endpoint
InetSocketAddress coapsAddr = new InetSocketAddress(config.getSecureHost(), config.getSecurePort());
endpointsBuilder.addEndpoint(coapsAddr, Protocol.COAPS);
builder.setDecoder(new DefaultLwM2mDecoder(true));
builder.setEncoder(new DefaultLwM2mEncoder(true));
// builder.setDecoder(new DefaultLwM2mDecoder());
// /* Use a magic converter to support bad type send by the UI. */
// builder.setEncoder(new DefaultLwM2mEncoder(LwM2mValueConverterImpl.getInstance()));
/* Create CoAP Config */
// builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort(), config));
// Create LWM2M server
builder.setEndpointsProviders(endpointsBuilder.build());
return builder.build(); return builder.build();
} }
private void setServerWithCredentials(LeshanServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) { private void setServerWithCredentials(LeshanServerBuilder builder) {
// private void setServerWithCredentials(LeshanServerBuilder builder) {
if (this.config.getSslCredentials() != null) { if (this.config.getSslCredentials() != null) {
SslCredentials sslCredentials = this.config.getSslCredentials(); SslCredentials sslCredentials = this.config.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey()); builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey()); builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain()); builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
builder.setAuthorizer(authorizer); builder.setAuthorizer(authorizer);
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else { } else {
/* by default trust all */ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]); builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for LWM2MServer");
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, PSK_CIPHER_SUITES);
} }
} }

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MNetworkConfig.java

@ -27,8 +27,8 @@ import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_S
public class LwM2MNetworkConfig { public class LwM2MNetworkConfig {
public static Configuration getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) { public static Configuration getCoapConfig(Configuration coapConfig, Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) {
Configuration coapConfig = new Configuration(); // Configuration coapConfig = new Configuration();
coapConfig.set(CoapConfig.COAP_PORT, serverPortNoSec); coapConfig.set(CoapConfig.COAP_PORT, serverPortNoSec);
coapConfig.set(CoapConfig.COAP_SECURE_PORT, serverSecurePort); coapConfig.set(CoapConfig.COAP_SECURE_PORT, serverSecurePort);
/** /**
@ -107,4 +107,4 @@ public class LwM2MNetworkConfig {
return coapConfig; return coapConfig;
} }
} }

12
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java

@ -16,7 +16,7 @@
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
import org.eclipse.leshan.core.observation.CompositeObservation; import org.eclipse.leshan.core.observation.CompositeObservation;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.observation.SingleObservation; import org.eclipse.leshan.core.observation.SingleObservation;
@ -32,7 +32,6 @@ import org.eclipse.leshan.server.send.SendListener;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertObjectIdToVersionedId;
@ -128,10 +127,15 @@ public class LwM2mServerListener {
public final SendListener sendListener = new SendListener() { public final SendListener sendListener = new SendListener() {
@Override @Override
public void dataReceived(Registration registration, Map<String, LwM2mNode> map, SendRequest sendRequest) { public void dataReceived(Registration registration, TimestampedLwM2mNodes data, SendRequest request) {
if (registration != null) { if (registration != null) {
service.onUpdateValueWithSendRequest(registration, sendRequest); service.onUpdateValueWithSendRequest(registration, request);
} }
} }
@Override
public void onError(Registration registration, String errorMessage, Exception error) {
}
}; };
} }

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java

@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.LeshanServer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;

10
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java

@ -16,7 +16,7 @@
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
@ -107,7 +107,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
@Override @Override
public ObjectModel getObjectModel(int objectId) { public ObjectModel getObjectModel(int objectId) {
String version = registration.getSupportedVersion(objectId); String version = String.valueOf(registration.getSupportedVersion(objectId));
if (version != null) { if (version != null) {
return this.getObjectModelDynamic(objectId, version); return this.getObjectModelDynamic(objectId, version);
} }
@ -116,10 +116,10 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
@Override @Override
public Collection<ObjectModel> getObjectModels() { public Collection<ObjectModel> getObjectModels() {
Map<Integer, String> supportedObjects = this.registration.getSupportedObject(); Map<Integer, LwM2m.Version> supportedObjects = this.registration.getSupportedObject();
Collection<ObjectModel> result = new ArrayList<>(supportedObjects.size()); Collection<ObjectModel> result = new ArrayList<>(supportedObjects.size());
for (Map.Entry<Integer, String> supportedObject : supportedObjects.entrySet()) { for (Map.Entry<Integer, LwM2m.Version> supportedObject : supportedObjects.entrySet()) {
ObjectModel objectModel = this.getObjectModelDynamic(supportedObject.getKey(), supportedObject.getValue()); ObjectModel objectModel = this.getObjectModelDynamic(supportedObject.getKey(), String.valueOf(supportedObject.getValue()));
if (objectModel != null) { if (objectModel != null) {
result.add(objectModel); result.add(objectModel);
} }

21
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java

@ -20,7 +20,8 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.link.LinkParamValue; import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.link.attributes.Attribute;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mMultipleResource;
@ -271,7 +272,7 @@ public class LwM2mClient {
public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) { public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) {
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verSupportedObject = String.valueOf(registration.getSupportedObject().get(pathIds.getObjectId()));
String verRez = getVerFromPathIdVerOrId(pathIdVer); String verRez = getVerFromPathIdVerOrId(pathIdVer);
return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
@ -289,7 +290,7 @@ public class LwM2mClient {
public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) { public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) {
try { try {
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verSupportedObject = String.valueOf(registration.getSupportedObject().get(pathIds.getObjectId()));
String verRez = getVerFromPathIdVerOrId(pathIdVer); String verRez = getVerFromPathIdVerOrId(pathIdVer);
return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()) : null; .getObjectModel(pathIds.getObjectId()) : null;
@ -370,12 +371,12 @@ public class LwM2mClient {
public String isValidObjectVersion(String path) { public String isValidObjectVersion(String path) {
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); LwM2m.Version verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
if (verSupportedObject == null) { if (verSupportedObject == null) {
return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId()); return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId());
} else { } else {
String verRez = getVerFromPathIdVerOrId(path); String verRez = getVerFromPathIdVerOrId(path);
if (verRez == null || !verRez.equals(verSupportedObject)) { if (verRez == null || !verRez.equals(verSupportedObject.toString())) {
return String.format("Specified resource id %s is not valid version! Must be version: %s", path, verSupportedObject); return String.format("Specified resource id %s is not valid version! Must be version: %s", path, verSupportedObject);
} }
} }
@ -432,16 +433,12 @@ public class LwM2mClient {
private static Set<ContentFormat> clientSupportContentFormat(Registration registration) { private static Set<ContentFormat> clientSupportContentFormat(Registration registration) {
Set<ContentFormat> contentFormats = new HashSet<>(); Set<ContentFormat> contentFormats = new HashSet<>();
contentFormats.add(ContentFormat.DEFAULT); contentFormats.add(ContentFormat.DEFAULT);
LinkParamValue ct = Arrays.stream(registration.getObjectLinks()) Attribute ct = Arrays.stream(registration.getObjectLinks())
.filter(link -> link.getUriReference().equals("/")) .filter(link -> link.getUriReference().equals("/"))
.findFirst() .findFirst()
.map(link -> link.getLinkParams().get("ct")).orElse(null); .map(link -> link.getAttributes().get("ct")).orElse(null);
if (ct != null) { if (ct != null) {
Set<ContentFormat> codes = Stream.of(ct.getUnquoted().replaceAll("\"", "").split(" ", -1)) Set codes = Stream.of(ct.getValue()).collect(Collectors.toSet());
.map(String::trim)
.map(Integer::parseInt)
.map(ContentFormat::fromCode)
.collect(Collectors.toSet());
contentFormats.addAll(codes); contentFormats.addAll(codes);
} }
return contentFormats; return contentFormats;

88
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java

@ -15,12 +15,15 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.downlink; package org.thingsboard.server.transport.lwm2m.server.downlink;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.LwM2m; import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.link.Link; import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttribute;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeModel;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
@ -75,8 +78,6 @@ import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogServic
import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcWriteCompositeRequest; import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcWriteCompositeRequest;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
@ -86,15 +87,16 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.GREATER_THAN;
import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.LESSER_THAN;
import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.MAXIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.MINIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.STEP; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.STEP;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody;
@ -148,8 +150,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override @Override
public void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request, public void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request,
DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback, ContentFormat compositeContentFormat) { DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback) {
try { try {
ContentFormat compositeContentFormat = this.findFirstContentFormatForComposite(client.getClientSupportContentFormats());
ReadCompositeRequest downlink = new ReadCompositeRequest(compositeContentFormat, compositeContentFormat, request.getObjectIds()); ReadCompositeRequest downlink = new ReadCompositeRequest(compositeContentFormat, compositeContentFormat, request.getObjectIds());
sendCompositeRequest(client, downlink, this.config.getTimeout(), callback); sendCompositeRequest(client, downlink, this.config.getTimeout(), callback);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
@ -193,8 +196,8 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} }
@Override @Override
public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<Link>> callback) { public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<String>> callback) {
callback.onSuccess(request, Arrays.asList(client.getRegistration().getSortedObjectLinks())); callback.onSuccess(request, Arrays.stream(client.getRegistration().getSortedObjectLinks()).map(Link::toCoreLinkFormat).collect(Collectors.toList()));
} }
@Override @Override
@ -279,13 +282,13 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
throw new IllegalArgumentException("Attributes to write are not specified!"); throw new IllegalArgumentException("Attributes to write are not specified!");
} }
ObjectAttributes params = request.getAttributes(); ObjectAttributes params = request.getAttributes();
List<Attribute> attributes = new LinkedList<>(); List<LwM2mAttribute<?>> attributes = new LinkedList<>();
addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax());
addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); addAttribute(attributes, MINIMUM_PERIOD, params.getPmin());
addAttribute(attributes, GREATER_THAN, params.getGt()); addAttribute(attributes, GREATER_THAN, params.getGt());
addAttribute(attributes, LESSER_THAN, params.getLt()); addAttribute(attributes, LESSER_THAN, params.getLt());
addAttribute(attributes, STEP, params.getSt()); addAttribute(attributes, STEP, params.getSt());
AttributeSet attributeSet = new AttributeSet(attributes); LwM2mAttributeSet attributeSet = new LwM2mAttributeSet(attributes);
sendSimpleRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); sendSimpleRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
callback.onValidationError(request.toString(), e.getMessage()); callback.onValidationError(request.toString(), e.getMessage());
@ -338,9 +341,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override @Override
public void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest rpcWriteCompositeRequest, public void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest rpcWriteCompositeRequest,
DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback, ContentFormat contentFormatComposite) { DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback) {
try { try {
WriteCompositeRequest downlink = new WriteCompositeRequest(contentFormatComposite, rpcWriteCompositeRequest.getNodes()); ContentFormat compositeContentFormat = this.findFirstContentFormatForComposite(client.getClientSupportContentFormats());
WriteCompositeRequest downlink = new WriteCompositeRequest(compositeContentFormat, rpcWriteCompositeRequest.getNodes());
//TODO: replace config.getTimeout(); //TODO: replace config.getTimeout();
sendWriteCompositeRequest(client, downlink, this.config.getTimeout(), callback); sendWriteCompositeRequest(client, downlink, this.config.getTimeout(), callback);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
@ -570,17 +574,18 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} }
} }
private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value) { private static <T> void addAttribute(List<LwM2mAttribute<?>> attributes, LwM2mAttributeModel<T> attribute, T value) {
addAttribute(attributes, attributeName, value, null, null); addAttribute(attributes, attribute, value, null, null);
} }
private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value, Function<T, ?> converter) { private static <T> void addAttribute(List<LwM2mAttribute<?>> attributes, LwM2mAttributeModel<T> attribute, T value, Function<T, ?> converter) {
addAttribute(attributes, attributeName, value, null, converter); addAttribute(attributes, attribute, value, null, converter);
} }
private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value, Predicate<T> filter, Function<T, ?> converter) { private static <T> void addAttribute(List<LwM2mAttribute<?>> attributes, LwM2mAttributeModel<T> attributeName, T value, Predicate<T> filter, Function<T, ?> converter) {
if (value != null && ((filter == null) || filter.test(value))) { if (value != null && ((filter == null) || filter.test(value))) {
attributes.add(new Attribute(attributeName, converter != null ? converter.apply(value) : value)); T valueConvert = (T) converter != null ? (T) converter.apply(value) : value;
attributes.add(new LwM2mAttribute<>(attributeName, valueConvert));
} }
} }
@ -620,7 +625,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} else if (OPAQUE.equals(resourceModel.type)) { } else if (OPAQUE.equals(resourceModel.type)) {
return ContentFormat.OPAQUE; return ContentFormat.OPAQUE;
} else { } else {
return findFirst(client.getClientSupportContentFormats(), client.getDefaultContentFormat(), ContentFormat.CBOR, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON); return findFirstContentFormatForComp(client.getClientSupportContentFormats(), client.getDefaultContentFormat(), ContentFormat.CBOR, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON);
} }
} else { } else {
return getContentFormatForComplex(client); return getContentFormatForComplex(client);
@ -634,7 +639,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
if (LwM2m.LwM2mVersion.V1_0.equals(client.getRegistration().getLwM2mVersion())) { if (LwM2m.LwM2mVersion.V1_0.equals(client.getRegistration().getLwM2mVersion())) {
return ContentFormat.TLV; return ContentFormat.TLV;
} else if (LwM2m.LwM2mVersion.V1_1.equals(client.getRegistration().getLwM2mVersion())) { } else if (LwM2m.LwM2mVersion.V1_1.equals(client.getRegistration().getLwM2mVersion())) {
ContentFormat result = findFirst(client.getClientSupportContentFormats(), null, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON, ContentFormat.TLV, ContentFormat.JSON); ContentFormat result = findFirstContentFormatForComp(client.getClientSupportContentFormats(), null, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON, ContentFormat.TLV, ContentFormat.JSON);
if (result != null) { if (result != null) {
return result; return result;
} else { } else {
@ -644,16 +649,6 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
throw new RuntimeException("The version " + client.getRegistration().getLwM2mVersion() + " is not supported!"); throw new RuntimeException("The version " + client.getRegistration().getLwM2mVersion() + " is not supported!");
} }
} }
private static ContentFormat findFirst(Set<ContentFormat> supported, ContentFormat defaultValue, ContentFormat... desiredFormats) {
for (ContentFormat contentFormat : desiredFormats) {
if (supported.contains(contentFormat)) {
return contentFormat;
}
}
return defaultValue;
}
private <R> String toString(R request) { private <R> String toString(R request) {
try { try {
return request != null ? request.toString() : ""; return request != null ? request.toString() : "";
@ -662,4 +657,29 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
return request.getClass().getSimpleName(); return request.getClass().getSimpleName();
} }
} }
private ContentFormat findFirstContentFormatForComposite (Set<?> clientSupportContentFormats) {
ContentFormat contentFormat = findFirstContentFormatForComp(clientSupportContentFormats, null, ContentFormat.SENML_JSON, ContentFormat.SENML_CBOR);
if (contentFormat != null) {
return contentFormat;
} else {
throw new RuntimeException("This device does not support Composite Operation");
}
}
private static ContentFormat findFirstContentFormatForComp(Set<?> clientSupportContentFormats, ContentFormat defaultValue, ContentFormat... desiredFormats) {
AtomicReference<ContentFormat> compositeContentFormat = new AtomicReference<>();
clientSupportContentFormats.forEach(c -> {
if (c instanceof Collection) {
for (ContentFormat contentFormat : desiredFormats) {
if (((Collection<?>) c).contains(contentFormat)) {
compositeContentFormat.set(contentFormat);
break;
}
}
} else if (compositeContentFormat.get() == null && c instanceof ContentFormat) {
compositeContentFormat.set(Arrays.stream(desiredFormats).filter(f -> f.equals(c)).findFirst().get());
}
});
return compositeContentFormat.get() != null ? compositeContentFormat.get() : defaultValue;
}
} }

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java

@ -15,8 +15,6 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.downlink; package org.thingsboard.server.transport.lwm2m.server.downlink;
import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.CreateRequest; import org.eclipse.leshan.core.request.CreateRequest;
import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.request.DeleteRequest;
import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.request.DiscoverRequest;
@ -48,7 +46,7 @@ public interface LwM2mDownlinkMsgHandler {
void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback<ReadRequest, ReadResponse> callback); void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback<ReadRequest, ReadResponse> callback);
void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request, DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback, ContentFormat contentFormatComposite); void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request, DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback);
void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback<ObserveRequest, ObserveResponse> callback); void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback<ObserveRequest, ObserveResponse> callback);
@ -64,13 +62,13 @@ public interface LwM2mDownlinkMsgHandler {
void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback); void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback);
void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<Link>> callback); void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<String>> callback);
void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback<WriteAttributesRequest, WriteAttributesResponse> callback); void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback<WriteAttributesRequest, WriteAttributesResponse> callback);
void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback); void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback);
void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest nodes, DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback, ContentFormat contentFormatComposite); void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest nodes, DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback);
void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback); void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback);

115
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java

@ -23,7 +23,6 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -75,6 +74,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertValueByTypeResource;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId;
@Slf4j @Slf4j
@ -155,22 +155,16 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
} }
} else if (operationType.isComposite()) { } else if (operationType.isComposite()) {
ContentFormat contentFormatComposite = this.getCompositeContentFormat(client);
if (contentFormatComposite != null) {
switch (operationType) { switch (operationType) {
case READ_COMPOSITE: case READ_COMPOSITE:
sendReadCompositeRequest(client, rpcRequest, contentFormatComposite); sendReadCompositeRequest(client, rpcRequest);
break; break;
case WRITE_COMPOSITE: case WRITE_COMPOSITE:
sendWriteCompositeRequest(client, rpcRequest, contentFormatComposite); sendWriteCompositeRequest(client, rpcRequest);
break; break;
default: default:
throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
} }
} else {
this.sendErrorRpcResponse(sessionInfo, rpcRequest.getRequestId(),
ResponseCode.INTERNAL_SERVER_ERROR, "This device does not support Composite Operation");
}
} else { } else {
switch (operationType) { switch (operationType) {
case OBSERVE_CANCEL_ALL: case OBSERVE_CANCEL_ALL:
@ -202,12 +196,12 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
downlinkHandler.sendReadRequest(client, request, rpcCallback); downlinkHandler.sendReadRequest(client, request, rpcCallback);
} }
private void sendReadCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, ContentFormat contentFormatComposite) { private void sendReadCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) {
String[] versionedIds = getIdsFromParameters(client, requestMsg); String[] versionedIds = getIdsFromParameters(client, requestMsg);
TbLwM2MReadCompositeRequest request = TbLwM2MReadCompositeRequest.builder().versionedIds(versionedIds).timeout(clientContext.getRequestTimeout(client)).build(); TbLwM2MReadCompositeRequest request = TbLwM2MReadCompositeRequest.builder().versionedIds(versionedIds).timeout(clientContext.getRequestTimeout(client)).build();
var mainCallback = new TbLwM2MReadCompositeCallback(uplinkHandler, logService, client, versionedIds); var mainCallback = new TbLwM2MReadCompositeCallback(uplinkHandler, logService, client, versionedIds);
var rpcCallback = new RpcReadResponseCompositeCallback(transportService, client, requestMsg, mainCallback); var rpcCallback = new RpcReadResponseCompositeCallback(transportService, client, requestMsg, mainCallback);
downlinkHandler.sendReadCompositeRequest(client, request, rpcCallback, contentFormatComposite); downlinkHandler.sendReadCompositeRequest(client, request, rpcCallback);
} }
private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
@ -243,7 +237,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
private void sendWriteAttributesRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { private void sendWriteAttributesRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
RpcWriteAttributesRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteAttributesRequest.class); RpcWriteAttributesRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteAttributesRequest.class);
TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId) TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId)
.attributes(requestBody.getAttributes()) .attributes(requestBody.getAttributes())
.timeout(clientContext.getRequestTimeout(client)).build(); .timeout(clientContext.getRequestTimeout(client)).build();
var mainCallback = new TbLwM2MWriteAttributesCallback(logService, client, versionedId); var mainCallback = new TbLwM2MWriteAttributesCallback(logService, client, versionedId);
@ -298,54 +292,89 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
* nodes.put("/1/0/2", 100); * nodes.put("/1/0/2", 100);
* nodes.put("/5/0/1", "coap://localhost:5685"); * nodes.put("/5/0/1", "coap://localhost:5685");
*/ */
private void sendWriteCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, ContentFormat contentFormatComposite) { private void sendWriteCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) {
RpcWriteCompositeRequest rpcWriteCompositeRequest = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteCompositeRequest.class); RpcWriteCompositeRequest rpcWriteCompositeRequest = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteCompositeRequest.class);
Map<String, Object> validNodes = validateNodes(client, rpcWriteCompositeRequest.getNodes()); Map<String, Object> validNodes = validateCompositesNodes(client, rpcWriteCompositeRequest.getNodes());
if (validNodes.size() > 0) { if (validNodes.size() > 0) {
rpcWriteCompositeRequest.setNodes(validNodes); rpcWriteCompositeRequest.setNodes(validNodes);
var mainCallback = new TbLwM2MWriteResponseCompositeCallback(uplinkHandler, logService, client, null); var mainCallback = new TbLwM2MWriteResponseCompositeCallback(uplinkHandler, logService, client, null);
var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback);
downlinkHandler.sendWriteCompositeRequest(client, rpcWriteCompositeRequest, rpcCallback, contentFormatComposite); downlinkHandler.sendWriteCompositeRequest(client, rpcWriteCompositeRequest, rpcCallback);
} else { } else {
throw new IllegalArgumentException(String.format("nodes: %s is not validate value", rpcWriteCompositeRequest.getNodes().toString())); throw new IllegalArgumentException(String.format("nodes: %s is not validate value", rpcWriteCompositeRequest.getNodes().toString()));
} }
} }
private Map<String, Object> validateNodes(LwM2mClient client, Map<String, Object> nodes) { private Map<String, Object> validateCompositesNodes(LwM2mClient client, Map<String, Object> nodes) {
Map<String, Object> newNodes = new LinkedHashMap<>(); Map<String, Object> newNodes = new LinkedHashMap<>();
nodes.forEach((key, value) -> { nodes.forEach((key, value) -> {
String versionedId; String versionedId = null;
LwM2mPath path = null;
try { try {
LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(key)); path = new LwM2mPath(fromVersionedIdToObjectId(key));
if (path.isResource() || path.isResourceInstance()) {
versionedId = key;
}
else {
throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString()));
}
} catch (Exception e) { } catch (Exception e) {
versionedId = clientContext.getObjectIdByKeyNameFromProfile(client, key); versionedId = clientContext.getObjectIdByKeyNameFromProfile(client, key);
} }
// validate value. Must be only primitive, not JsonObject or JsonArray
try { if (versionedId == null) {
JsonElement element = JsonUtils.parse(value.toString()); if (path.isResourceInstance()) {
if (!element.isJsonNull() && !element.isJsonPrimitive()) { setValueToCompositeNodes(client, newNodes, nodes, key, value.toString());
} else if (path.isResource()) {
validateResource(client, newNodes, nodes, key , value);
} else if (path.isObjectInstance() && value instanceof Map<?, ?>) {
((Map) value).forEach((k, v) -> {
validateResource(client, newNodes, nodes, validateResourceId (key, k.toString(), nodes), v);
});
} else {
throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " + throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString())); "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes));
}
else if (versionedId != null) {
newNodes.put(fromVersionedIdToObjectId(versionedId), value);
}
} catch (JsonSyntaxException jse) {
if (versionedId != null) {
newNodes.put(fromVersionedIdToObjectId(versionedId), value);
} }
} else {
setValueToCompositeNodes(client, newNodes, nodes, versionedId, value.toString());
} }
}); });
return newNodes; return newNodes;
} }
private void validateResource(LwM2mClient client, Map newNodes, Map nodes, String resourceId , Object value) {
if (value instanceof Map<?, ?>) {
((Map<?, ?>) value).forEach((k, v) -> {
setValueToCompositeNodes(client, newNodes, nodes, validateResourceId (resourceId, k.toString(), nodes), v.toString());
});
} else {
setValueToCompositeNodes(client, newNodes, nodes, resourceId, value.toString());
}
}
private String validateResourceId (String key, String id, Map nodes) {
try {
Integer.parseInt(id);
return key + "/" + id;
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString()));
}
}
private void setValueToCompositeNodes (LwM2mClient client, Map newNodes, Map nodes, String versionedId , String value) {
// validate value. Must be only primitive, not JsonObject or JsonArray
try {
JsonElement element = JsonUtils.parse(value);
if (!element.isJsonNull() && !element.isJsonPrimitive()) {
throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString()));
}
// convert value from JsonPrimitive() to resource/ResourceInstance type
ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider);
Object newValue = convertValueByTypeResource(value, resourceModel.type, versionedId);
// add new value after convert
newNodes.put(fromVersionedIdToObjectId(versionedId), newValue);
} catch (JsonSyntaxException jse) {
newNodes.put(fromVersionedIdToObjectId(versionedId), value);
}
}
private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(clientContext.getRequestTimeout(client)).build(); TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(clientContext.getRequestTimeout(client)).build();
var mainCallback = new TbLwM2MCancelObserveCallback(logService, client, versionedId); var mainCallback = new TbLwM2MCancelObserveCallback(logService, client, versionedId);
@ -412,16 +441,4 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
log.info("[{}] toServerRpcResponse", toServerResponse); log.info("[{}] toServerRpcResponse", toServerResponse);
} }
private ContentFormat getCompositeContentFormat(LwM2mClient client) {
if (client.getClientSupportContentFormats().contains(ContentFormat.SENML_JSON)) {
return ContentFormat.SENML_JSON;
}
else if (client.getClientSupportContentFormats().contains(ContentFormat.SENML_CBOR)) {
return ContentFormat.SENML_CBOR;
}
else {
return null;
}
}
} }

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java

@ -35,7 +35,7 @@ public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback<DiscoverReques
} }
protected Optional<String> serializeSuccessfulResponse(DiscoverResponse response) { protected Optional<String> serializeSuccessfulResponse(DiscoverResponse response) {
return Optional.of(serializer.serialize(response.getObjectLinks())); return Optional.of(serializer.serializeCoreLinkFormat(response.getObjectLinks()));
} }
} }

16
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.server.store; package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.peer.OscoreIdentity;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
@ -83,25 +84,30 @@ public class TbInMemorySecurityStore implements TbEditableSecurityStore {
} }
} }
@Override
public SecurityInfo getByOscoreIdentity(OscoreIdentity oscoreIdentity) {
return null;
}
@Override @Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException { public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
writeLock.lock(); writeLock.lock();
try { try {
String identity = null; String identity = null;
if (tbSecurityInfo.getSecurityInfo() != null) { if (tbSecurityInfo.getSecurityInfo() != null) {
identity = tbSecurityInfo.getSecurityInfo().getIdentity(); identity = tbSecurityInfo.getSecurityInfo().getPskIdentity();
if (identity != null) { if (identity != null) {
TbLwM2MSecurityInfo infoByIdentity = securityByIdentity.get(identity); TbLwM2MSecurityInfo infoByIdentity = securityByIdentity.get(identity);
if (infoByIdentity != null && !tbSecurityInfo.getSecurityInfo().getEndpoint().equals(infoByIdentity.getEndpoint())) { if (infoByIdentity != null && !tbSecurityInfo.getSecurityInfo().getEndpoint().equals(infoByIdentity.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + identity + " is already used"); throw new NonUniqueSecurityInfoException("PSK Identity " + identity + " is already used");
} }
securityByIdentity.put(tbSecurityInfo.getSecurityInfo().getIdentity(), tbSecurityInfo); securityByIdentity.put(tbSecurityInfo.getSecurityInfo().getPskIdentity(), tbSecurityInfo);
} }
} }
TbLwM2MSecurityInfo previous = securityByEp.put(tbSecurityInfo.getEndpoint(), tbSecurityInfo); TbLwM2MSecurityInfo previous = securityByEp.put(tbSecurityInfo.getEndpoint(), tbSecurityInfo);
if (previous != null && previous.getSecurityInfo() != null) { if (previous != null && previous.getSecurityInfo() != null) {
String previousIdentity = previous.getSecurityInfo().getIdentity(); String previousIdentity = previous.getSecurityInfo().getPskIdentity();
if (previousIdentity != null && !previousIdentity.equals(identity)) { if (previousIdentity != null && !previousIdentity.equals(identity)) {
securityByIdentity.remove(previousIdentity); securityByIdentity.remove(previousIdentity);
} }
@ -116,8 +122,8 @@ public class TbInMemorySecurityStore implements TbEditableSecurityStore {
writeLock.lock(); writeLock.lock();
try { try {
TbLwM2MSecurityInfo securityInfo = securityByEp.remove(endpoint); TbLwM2MSecurityInfo securityInfo = securityByEp.remove(endpoint);
if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getIdentity() != null) { if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getPskIdentity() != null) {
securityByIdentity.remove(securityInfo.getSecurityInfo().getIdentity()); securityByIdentity.remove(securityInfo.getSecurityInfo().getPskIdentity());
} }
} finally { } finally {
writeLock.unlock(); writeLock.unlock();

99
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java

@ -16,6 +16,8 @@
package org.thingsboard.server.transport.lwm2m.server.store; package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.californium.core.coap.Token; import org.eclipse.californium.core.coap.Token;
import org.eclipse.californium.core.network.serialization.UdpDataParser;
import org.eclipse.californium.core.network.serialization.UdpDataSerializer;
import org.eclipse.californium.core.observe.ObservationStoreException; import org.eclipse.californium.core.observe.ObservationStoreException;
import org.eclipse.californium.elements.EndpointContext; import org.eclipse.californium.elements.EndpointContext;
import org.eclipse.leshan.core.Destroyable; import org.eclipse.leshan.core.Destroyable;
@ -23,18 +25,18 @@ import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable; import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.californium.ObserveUtil; import org.eclipse.leshan.core.californium.ObserveUtil;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.observation.ObservationIdentifier;
import org.eclipse.leshan.core.observation.SingleObservation; import org.eclipse.leshan.core.observation.SingleObservation;
import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.peer.LwM2mIdentity;
import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.eclipse.leshan.core.util.Validate; import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.observation.ObservationSerDes;
import org.eclipse.leshan.server.redis.RedisRegistrationStore; import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.serialization.IdentitySerDes;
import org.eclipse.leshan.server.redis.serialization.ObservationSerDes;
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes; import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
import org.eclipse.leshan.server.registration.Deregistration; import org.eclipse.leshan.server.registration.Deregistration;
import org.eclipse.leshan.server.registration.ExpirationListener; import org.eclipse.leshan.server.registration.ExpirationListener;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.registration.RegistrationStore;
import org.eclipse.leshan.server.registration.RegistrationUpdate; import org.eclipse.leshan.server.registration.RegistrationUpdate;
import org.eclipse.leshan.server.registration.UpdatedRegistration; import org.eclipse.leshan.server.registration.UpdatedRegistration;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -63,7 +65,7 @@ import java.util.concurrent.locks.Lock;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationStore, Startable, Stoppable, Destroyable { public class TbLwM2mRedisRegistrationStore implements RegistrationStore, Startable, Stoppable, Destroyable {
/** Default time in seconds between 2 cleaning tasks (used to remove expired registration). */ /** Default time in seconds between 2 cleaning tasks (used to remove expired registration). */
public static final long DEFAULT_CLEAN_PERIOD = 60; public static final long DEFAULT_CLEAN_PERIOD = 60;
public static final int DEFAULT_CLEAN_LIMIT = 500; public static final int DEFAULT_CLEAN_LIMIT = 500;
@ -83,6 +85,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
private static final byte[] EXP_EP = "EXP:EP".getBytes(UTF_8); // a sorted set used for registration expiration private static final byte[] EXP_EP = "EXP:EP".getBytes(UTF_8); // a sorted set used for registration expiration
// (expiration date, Endpoint) // (expiration date, Endpoint)
private final RegistrationSerDes registrationSerDes = new RegistrationSerDes();
private final ObservationSerDes observationSerDes = new ObservationSerDes(new UdpDataParser(), new UdpDataSerializer());
private final RedisConnectionFactory connectionFactory; private final RedisConnectionFactory connectionFactory;
// Listener use to notify when a registration expires // Listener use to notify when a registration expires
@ -159,7 +163,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] addr_idx = toRegAddrKey(registration.getSocketAddress()); byte[] addr_idx = toRegAddrKey(registration.getSocketAddress());
connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] identity_idx = toRegIdentityKey(registration.getIdentity()); byte[] identity_idx = toRegIdentityKey(registration.getClientTransportData().getIdentity());
connection.set(identity_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(identity_idx, registration.getEndpoint().getBytes(UTF_8));
// Add or update expiration // Add or update expiration
@ -173,7 +177,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) { if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) {
removeAddrIndex(connection, oldRegistration); removeAddrIndex(connection, oldRegistration);
} }
if (!oldRegistration.getIdentity().equals(registration.getIdentity())) { if (!oldRegistration.getClientTransportData().getIdentity().equals(registration.getClientTransportData().getIdentity())) {
removeIdentityIndex(connection, oldRegistration); removeIdentityIndex(connection, oldRegistration);
} }
// remove old observation // remove old observation
@ -231,7 +235,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) { if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
removeAddrIndex(connection, r); removeAddrIndex(connection, r);
} }
if (!r.getIdentity().equals(updatedRegistration.getIdentity())) { if (!r.getClientTransportData().getIdentity().equals(updatedRegistration.getClientTransportData().getIdentity())) {
removeIdentityIndex(connection, r); removeIdentityIndex(connection, r);
} }
@ -281,7 +285,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
@Override @Override
public Registration getRegistrationByIdentity(Identity identity) { public Registration getRegistrationByIdentity(LwM2mIdentity identity) {
Validate.notNull(identity); Validate.notNull(identity);
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
byte[] ep = connection.get(toRegIdentityKey(identity)); byte[] ep = connection.get(toRegIdentityKey(identity));
@ -327,6 +331,26 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
} }
@Override
public Collection<Observation> addObservation(String registrationId, Observation observation, boolean addIfAbsent) {
return null;
}
@Override
public Observation getObservation(String registrationId, ObservationIdentifier observationId) {
return null;
}
@Override
public Observation getObservation(ObservationIdentifier observationId) {
return null;
}
@Override
public Observation removeObservation(String registrationId, ObservationIdentifier observationId) {
return null;
}
private Deregistration removeRegistration(RedisConnection connection, String registrationId, boolean removeOnlyIfNotAlive) { private Deregistration removeRegistration(RedisConnection connection, String registrationId, boolean removeOnlyIfNotAlive) {
// fetch the client ep by registration ID index // fetch the client ep by registration ID index
byte[] ep = connection.get(toRegIdKey(registrationId)); byte[] ep = connection.get(toRegIdKey(registrationId));
@ -371,7 +395,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
private void removeIdentityIndex(RedisConnection connection, Registration r) { private void removeIdentityIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegIdentityKey(r.getIdentity()), r.getEndpoint()); removeSecondaryIndex(connection, toRegIdentityKey(r.getClientTransportData().getIdentity()), r.getEndpoint());
} }
//TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies //TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies
@ -410,8 +434,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort()); return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort());
} }
private byte[] toRegIdentityKey(Identity identity) { private byte[] toRegIdentityKey(LwM2mIdentity identity) {
return toKey(REG_EP_IDENTITY, IdentitySerDes.serialize(identity).toString()); return toKey(REG_EP_IDENTITY, identity.toString());
} }
private byte[] toEndpointKey(String endpoint) { private byte[] toEndpointKey(String endpoint) {
@ -423,11 +447,11 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
private byte[] serializeReg(Registration registration) { private byte[] serializeReg(Registration registration) {
return RegistrationSerDes.bSerialize(registration); return registrationSerDes.bSerialize(registration);
} }
private Registration deserializeReg(byte[] data) { private Registration deserializeReg(byte[] data) {
return RegistrationSerDes.deserialize(data); return registrationSerDes.deserialize(data);
} }
/* *************** Leshan Observation API **************** */ /* *************** Leshan Observation API **************** */
@ -436,7 +460,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
* The observation is not persisted here, it is done by the Californium layer (in the implementation of the * The observation is not persisted here, it is done by the Californium layer (in the implementation of the
* org.eclipse.californium.core.observe.ObservationStore#add method) * org.eclipse.californium.core.observe.ObservationStore#add method)
*/ */
@Override
public Collection<Observation> addObservation(String registrationId, Observation observation) { public Collection<Observation> addObservation(String registrationId, Observation observation) {
List<Observation> removed = new ArrayList<>(); List<Observation> removed = new ArrayList<>();
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
@ -458,9 +482,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
for (Observation obs : getObservations(connection, registrationId)) { for (Observation obs : getObservations(connection, registrationId)) {
//TODO: should be able to use CompositeObservation //TODO: should be able to use CompositeObservation
if (((SingleObservation)observation).getPath().equals(((SingleObservation)obs).getPath()) if (((SingleObservation)observation).getPath().equals(((SingleObservation)obs).getPath())
&& !Arrays.equals(observation.getId(), obs.getId())) { && !observation.getId().equals(obs.getId())) {
removed.add(obs); removed.add(obs);
unsafeRemoveObservation(connection, registrationId, obs.getId()); unsafeRemoveObservation(connection, registrationId, obs.getId().getBytes());
} }
} }
@ -473,7 +497,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return removed; return removed;
} }
@Override
public Observation removeObservation(String registrationId, byte[] observationId) { public Observation removeObservation(String registrationId, byte[] observationId) {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
@ -490,7 +513,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
lock = redisLock.obtain(lockKey); lock = redisLock.obtain(lockKey);
lock.lock(); lock.lock();
Observation observation = build(get(new Token(observationId))); // Observation observation = build(get(new Token(observationId)));
Observation observation = build(null);
if (observation != null && registrationId.equals(observation.getRegistrationId())) { if (observation != null && registrationId.equals(observation.getRegistrationId())) {
unsafeRemoveObservation(connection, registrationId, observationId); unsafeRemoveObservation(connection, registrationId, observationId);
return observation; return observation;
@ -505,9 +529,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
} }
@Override
public Observation getObservation(String registrationId, byte[] observationId) { public Observation getObservation(String registrationId, byte[] observationId) {
return build(get(new Token(observationId))); // return build(get(new Token(observationId)));
return build(null);
} }
@Override @Override
@ -554,13 +578,11 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
/* *************** Californium ObservationStore API **************** */ /* *************** Californium ObservationStore API **************** */
@Override
public org.eclipse.californium.core.observe.Observation putIfAbsent(Token token, public org.eclipse.californium.core.observe.Observation putIfAbsent(Token token,
org.eclipse.californium.core.observe.Observation obs) throws ObservationStoreException { org.eclipse.californium.core.observe.Observation obs) throws ObservationStoreException {
return add(obs, true); return add(obs, true);
} }
@Override
public org.eclipse.californium.core.observe.Observation put(Token token, public org.eclipse.californium.core.observe.Observation put(Token token,
org.eclipse.californium.core.observe.Observation obs) throws ObservationStoreException { org.eclipse.californium.core.observe.Observation obs) throws ObservationStoreException {
return add(obs, false); return add(obs, false);
@ -588,7 +610,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (previousValue == null || previousValue.length == 0) { if (previousValue == null || previousValue.length == 0) {
connection.set(key, serializeObs); connection.set(key, serializeObs);
} else { } else {
return deserializeObs(previousValue); // return deserializeObs(previousValue);
return null;
} }
} else { } else {
previousValue = connection.getSet(key, serializeObs); previousValue = connection.getSet(key, serializeObs);
@ -599,7 +622,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
// log any collisions // log any collisions
if (previousValue != null && previousValue.length != 0) { if (previousValue != null && previousValue.length != 0) {
previousObservation = deserializeObs(previousValue); // previousObservation = deserializeObs(previousValue);
previousObservation = null;
LOG.warn( LOG.warn(
"Token collision ? observation from request [{}] will be replaced by observation from request [{}] ", "Token collision ? observation from request [{}] will be replaced by observation from request [{}] ",
previousObservation.getRequest(), obs.getRequest()); previousObservation.getRequest(), obs.getRequest());
@ -613,7 +637,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return previousObservation; return previousObservation;
} }
@Override
public void remove(Token token) { public void remove(Token token) {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
byte[] tokenKey = toKey(OBS_TKN, token.getBytes()); byte[] tokenKey = toKey(OBS_TKN, token.getBytes());
@ -623,7 +646,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (serializedObs == null) if (serializedObs == null)
return; return;
org.eclipse.californium.core.observe.Observation obs = deserializeObs(serializedObs); // org.eclipse.californium.core.observe.Observation obs = deserializeObs(serializedObs);
org.eclipse.californium.core.observe.Observation obs = null;
String registrationId = ObserveUtil.extractRegistrationId(obs); String registrationId = ObserveUtil.extractRegistrationId(obs);
Registration registration = getRegistration(connection, registrationId); Registration registration = getRegistration(connection, registrationId);
if (registration == null) { if (registration == null) {
@ -649,14 +673,14 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
@Override
public org.eclipse.californium.core.observe.Observation get(Token token) { public org.eclipse.californium.core.observe.Observation get(Token token) {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
byte[] obs = connection.get(toKey(OBS_TKN, token.getBytes())); byte[] obs = connection.get(toKey(OBS_TKN, token.getBytes()));
if (obs == null) { if (obs == null) {
return null; return null;
} else { } else {
return deserializeObs(obs); // return deserializeObs(obs);
return null;
} }
} }
} }
@ -699,24 +723,26 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return removed; return removed;
} }
@Override
public void setContext(Token token, EndpointContext correlationContext) { public void setContext(Token token, EndpointContext correlationContext) {
// In Leshan we always set context when we send the request, so this should not be needed to implement this. // In Leshan we always set context when we send the request, so this should not be needed to implement this.
} }
private byte[] serializeObs(org.eclipse.californium.core.observe.Observation obs) { private byte[] serializeObs(org.eclipse.californium.core.observe.Observation obs) {
return ObservationSerDes.serialize(obs); return null;
// return observationSerDes.serialize(obs);
} }
private org.eclipse.californium.core.observe.Observation deserializeObs(byte[] data) { private Observation deserializeObs(byte[] data) {
return ObservationSerDes.deserialize(data); // return observationSerDes.deserialize(data);
return null;
} }
private Observation build(org.eclipse.californium.core.observe.Observation cfObs) { private Observation build(Observation cfObs) {
if (cfObs == null) if (cfObs == null)
return null; return null;
// String serializedObservation = observationSerDes.serialize(cfObs);
return ObserveUtil.createLwM2mObservation(cfObs.getRequest()); // return ObserveUtil.createLwM2mObservation(cfObs, serializedObservation);
return null;
} }
/* *************** Expiration handling **************** */ /* *************** Expiration handling **************** */
@ -787,7 +813,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
expirationListener = listener; expirationListener = listener;
} }
@Override
public void setExecutor(ScheduledExecutorService executor) { public void setExecutor(ScheduledExecutorService executor) {
// TODO should we reuse californium executor ? // TODO should we reuse californium executor ?
} }

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.server.store; package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.peer.OscoreIdentity;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
@ -86,6 +87,11 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
} }
} }
@Override
public SecurityInfo getByOscoreIdentity(OscoreIdentity oscoreIdentity) {
return null;
}
@Override @Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException { public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
SecurityInfo info = tbSecurityInfo.getSecurityInfo(); SecurityInfo info = tbSecurityInfo.getSecurityInfo();
@ -94,21 +100,21 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(tbSecurityInfo.getEndpoint()); lock = redisLock.obtain(tbSecurityInfo.getEndpoint());
lock.lock(); lock.lock();
if (info != null && info.getIdentity() != null) { if (info != null && info.getPskIdentity() != null) {
byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()); byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getPskIdentity().getBytes());
if (oldEndpointBytes != null) { if (oldEndpointBytes != null) {
String oldEndpoint = new String(oldEndpointBytes); String oldEndpoint = new String(oldEndpointBytes);
if (!oldEndpoint.equals(info.getEndpoint())) { if (!oldEndpoint.equals(info.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used"); throw new NonUniqueSecurityInfoException("PSK Identity " + info.getPskIdentity() + " is already used");
} }
connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes()); connection.hSet(PSKID_SEC.getBytes(), info.getPskIdentity().getBytes(), info.getEndpoint().getBytes());
} }
} }
byte[] previousData = connection.getSet((SEC_EP + tbSecurityInfo.getEndpoint()).getBytes(), tbSecurityInfoSerialized); byte[] previousData = connection.getSet((SEC_EP + tbSecurityInfo.getEndpoint()).getBytes(), tbSecurityInfoSerialized);
if (previousData != null && info != null) { if (previousData != null && info != null) {
String previousIdentity = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(previousData)).getSecurityInfo().getIdentity(); String previousIdentity = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(previousData)).getSecurityInfo().getPskIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) { if (previousIdentity != null && !previousIdentity.equals(info.getPskIdentity())) {
connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes()); connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
} }
} }
@ -147,8 +153,8 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
byte[] data = connection.get((SEC_EP + endpoint).getBytes()); byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data != null && data.length > 0) { if (data != null && data.length > 0) {
SecurityInfo info = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(data)).getSecurityInfo(); SecurityInfo info = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(data)).getSecurityInfo();
if (info != null && info.getIdentity() != null) { if (info != null && info.getPskIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes()); connection.hDel(PSKID_SEC.getBytes(), info.getPskIdentity().getBytes());
} }
connection.del((SEC_EP + endpoint).getBytes()); connection.del((SEC_EP + endpoint).getBytes());
} }

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.store;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.peer.OscoreIdentity;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
@ -60,7 +61,7 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore {
if (securityInfo == null) { if (securityInfo == null) {
securityInfo = fetchAndPutSecurityInfo(endpoint); securityInfo = fetchAndPutSecurityInfo(endpoint);
} else if (securityInfo.usePSK() && securityInfo.getEndpoint().equals(SecurityMode.NO_SEC.toString()) } else if (securityInfo.usePSK() && securityInfo.getEndpoint().equals(SecurityMode.NO_SEC.toString())
&& securityInfo.getIdentity().equals(SecurityMode.NO_SEC.toString()) && securityInfo.getPskIdentity().equals(SecurityMode.NO_SEC.toString())
&& Arrays.equals(SecurityMode.NO_SEC.toString().getBytes(), securityInfo.getPreSharedKey())) { && Arrays.equals(SecurityMode.NO_SEC.toString().getBytes(), securityInfo.getPreSharedKey())) {
return null; return null;
} }
@ -81,6 +82,11 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore {
return securityInfo; return securityInfo;
} }
@Override
public SecurityInfo getByOscoreIdentity(OscoreIdentity oscoreIdentity) {
return null;
}
public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) { public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) {
TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT); TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT);
doPut(securityInfo); doPut(securityInfo);

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java

@ -16,8 +16,8 @@
package org.thingsboard.server.transport.lwm2m.server.store; package org.thingsboard.server.transport.lwm2m.server.store;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.registration.InMemoryRegistrationStore;
import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore; import org.eclipse.leshan.server.registration.RegistrationStore;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -38,7 +38,7 @@ public class TbLwM2mStoreFactory {
private final LwM2mCredentialsSecurityInfoValidator validator; private final LwM2mCredentialsSecurityInfoValidator validator;
@Bean @Bean
private CaliforniumRegistrationStore registrationStore() { private RegistrationStore registrationStore() {
return redisConfiguration.isPresent() ? return redisConfiguration.isPresent() ?
new TbLwM2mRedisRegistrationStore(getConnectionFactory()) : new InMemoryRegistrationStore(config.getCleanPeriodInSec()); new TbLwM2mRedisRegistrationStore(getConnectionFactory()) : new InMemoryRegistrationStore(config.getCleanPeriodInSec());
} }

383
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java

@ -15,9 +15,8 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.store.util; package org.thingsboard.server.transport.lwm2m.server.store.util;
import com.eclipsesource.json.Json; import com.fasterxml.jackson.annotation.JsonValue;
import com.eclipsesource.json.JsonObject; import com.google.gson.JsonObject;
import com.eclipsesource.json.JsonValue;
import com.google.protobuf.util.JsonFormat; import com.google.protobuf.util.JsonFormat;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
@ -26,113 +25,107 @@ import org.eclipse.leshan.core.node.LwM2mNodeException;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.util.datatype.ULong;
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes; import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
import org.thingsboard.server.common.data.device.data.PowerMode;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Base64; import java.util.Base64;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
public class LwM2MClientSerDes { public class LwM2MClientSerDes {
public static final String VALUE = "value"; public static final String VALUE = "value";
@SneakyThrows @SneakyThrows
public static byte[] serialize(LwM2mClient client) { public static byte[] serialize(LwM2mClient client) {
JsonObject o = Json.object(); JsonObject o = new JsonObject();
o.add("nodeId", client.getNodeId()); o.addProperty("nodeId", "client.getNodeId()");
o.add("endpoint", client.getEndpoint()); o.addProperty("endpoint", client.getEndpoint());
JsonObject resources = Json.object(); JsonObject resources = new JsonObject();
client.getResources().forEach((k, v) -> { client.getResources().forEach((k, v) -> {
JsonObject resourceValue = Json.object(); JsonObject resourceValue = new JsonObject();
resourceValue.add("lwM2mResource", serialize(v.getLwM2mResource())); resourceValue.add("lwM2mResource", serialize(v.getLwM2mResource()));
resourceValue.add("resourceModel", serialize(v.getResourceModel())); resourceValue.add("resourceModel", serialize(v.getResourceModel()));
resources.add(k, resourceValue); resources.add(k, resourceValue);
}); });
o.add("resources", resources); o.add("resources", resources);
JsonObject sharedAttributes = Json.object(); JsonObject sharedAttributes = new JsonObject();
for (Map.Entry<String, TransportProtos.TsKvProto> entry : client.getSharedAttributes().entrySet()) { for (Map.Entry<String, TransportProtos.TsKvProto> entry : client.getSharedAttributes().entrySet()) {
sharedAttributes.add(entry.getKey(), JsonFormat.printer().print(entry.getValue())); sharedAttributes.addProperty(entry.getKey(), JsonFormat.printer().print(entry.getValue()));
} }
o.add("sharedAttributes", sharedAttributes); o.add("sharedAttributes", sharedAttributes);
JsonObject keyTsLatestMap = Json.object(); JsonObject keyTsLatestMap = new JsonObject();
client.getKeyTsLatestMap().forEach((k, v) -> { client.getKeyTsLatestMap().forEach((k, v) -> {
keyTsLatestMap.add(k, v.get()); keyTsLatestMap.addProperty(k, v.get());
}); });
o.add("keyTsLatestMap", keyTsLatestMap); o.add("keyTsLatestMap", keyTsLatestMap);
o.add("state", client.getState().toString()); o.addProperty("state", client.getState().toString());
if (client.getSession() != null) { if (client.getSession() != null) {
o.add("session", JsonFormat.printer().print(client.getSession())); o.addProperty("session", JsonFormat.printer().print(client.getSession()));
} }
if (client.getTenantId() != null) { if (client.getTenantId() != null) {
o.add("tenantId", client.getTenantId().toString()); o.addProperty("tenantId", client.getTenantId().toString());
} }
if (client.getDeviceId() != null) { if (client.getDeviceId() != null) {
o.add("deviceId", client.getDeviceId().toString()); o.addProperty("deviceId", client.getDeviceId().toString());
} }
if (client.getProfileId() != null) { if (client.getProfileId() != null) {
o.add("profileId", client.getProfileId().toString()); o.addProperty("profileId", client.getProfileId().toString());
} }
if (client.getPowerMode() != null) { if (client.getPowerMode() != null) {
o.add("powerMode", client.getPowerMode().toString()); o.addProperty("powerMode", client.getPowerMode().toString());
} }
if (client.getEdrxCycle() != null) { if (client.getEdrxCycle() != null) {
o.add("edrxCycle", client.getEdrxCycle()); o.addProperty("edrxCycle", client.getEdrxCycle());
} }
if (client.getPsmActivityTimer() != null) { if (client.getPsmActivityTimer() != null) {
o.add("psmActivityTimer", client.getPsmActivityTimer()); o.addProperty("psmActivityTimer", client.getPsmActivityTimer());
} }
if (client.getPagingTransmissionWindow() != null) { if (client.getPagingTransmissionWindow() != null) {
o.add("pagingTransmissionWindow", client.getPagingTransmissionWindow()); o.addProperty("pagingTransmissionWindow", client.getPagingTransmissionWindow());
} }
if (client.getRegistration() != null) { if (client.getRegistration() != null) {
o.add("registration", RegistrationSerDes.jSerialize(client.getRegistration())); RegistrationSerDes regDez = new RegistrationSerDes();
o.addProperty("registration", regDez.jSerialize(client.getRegistration()).toString());
} }
o.add("asleep", client.isAsleep()); o.addProperty("asleep", client.isAsleep());
o.add("lastUplinkTime", client.getLastUplinkTime()); o.addProperty("lastUplinkTime", client.getLastUplinkTime());
Field firstEdrxDownlink = LwM2mClient.class.getDeclaredField("firstEdrxDownlink"); Field firstEdrxDownlink = LwM2mClient.class.getDeclaredField("firstEdrxDownlink");
firstEdrxDownlink.setAccessible(true); firstEdrxDownlink.setAccessible(true);
o.add("firstEdrxDownlink", (boolean) firstEdrxDownlink.get(client)); o.addProperty("firstEdrxDownlink", (boolean) firstEdrxDownlink.get(client));
o.add("retryAttempts", client.getRetryAttempts().get()); o.addProperty("retryAttempts", client.getRetryAttempts().get());
if (client.getLastSentRpcId() != null) { if (client.getLastSentRpcId() != null) {
o.add("lastSentRpcId", client.getLastSentRpcId().toString()); o.addProperty("lastSentRpcId", client.getLastSentRpcId().toString());
} }
return o.toString().getBytes(); return o.toString().getBytes();
} }
private static JsonObject serialize(LwM2mResource resource) { private static JsonObject serialize(LwM2mResource resource) {
JsonObject o = Json.object(); JsonObject o = new JsonObject();
o.add("id", resource.getId()); o.addProperty("id", resource.getId());
o.add("type", resource.getType().toString()); o.addProperty("type", resource.getType().toString());
if (resource.isMultiInstances()) { if (resource.isMultiInstances()) {
o.add("multiInstances", true); o.addProperty("multiInstances", true);
JsonObject instances = Json.object(); JsonObject instances = new JsonObject();
resource.getInstances().forEach((id, in) -> { resource.getInstances().forEach((id, in) -> {
JsonObject instance = Json.object(); JsonObject instance = new JsonObject();
instance.add("id", in.getId()); instance.addProperty("id", in.getId());
addValue(instance, in.getType(), in.getValue()); addValue(instance, in.getType(), in.getValue());
instances.add(id.toString(), instance); instances.add(id.toString(), instance);
}); });
o.add("instances", instances); o.add("instances", instances);
} else { } else {
o.add("multiInstances", false); o.addProperty("multiInstances", false);
addValue(o, resource.getType(), resource.getValue()); addValue(o, resource.getType(), resource.getValue());
} }
@ -140,95 +133,95 @@ public class LwM2MClientSerDes {
} }
private static LwM2mResource parseLwM2mResource(JsonObject o) { private static LwM2mResource parseLwM2mResource(JsonObject o) {
boolean multiInstances = o.get("multiInstances").asBoolean(); boolean multiInstances = o.get("multiInstances").getAsBoolean();
int id = o.get("id").asInt(); int id = o.get("id").getAsInt();
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").asString()); ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").getAsString());
if (multiInstances) { if (multiInstances) {
Map<Integer, Object> instances = new HashMap<>(); Map<Integer, Object> instances = new HashMap<>();
o.get("instances").asObject().forEach(entry -> { o.get("instances").getAsJsonArray().forEach(entry -> {
instances.put(Integer.valueOf(entry.getName()), parseValue(type, entry.getValue())); // instances.put(Integer.valueOf(entry.getAsJsonObject().), parseValue(type, entry.getValue()));
}); });
return LwM2mMultipleResource.newResource(id, instances, type); return LwM2mMultipleResource.newResource(id, instances, type);
} else { } else {
return LwM2mSingleResource.newResource(id, parseValue(type, o.get(VALUE))); return LwM2mSingleResource.newResource(id, parseValue(type, (JsonValue) o.get(VALUE)));
} }
} }
private static Object parseValue(ResourceModel.Type type, JsonValue value) { private static Object parseValue(ResourceModel.Type type, JsonValue value) {
switch (type) { switch (type) {
case INTEGER: case INTEGER:
return value.asLong(); return value.value();
case FLOAT: // case FLOAT:
return value.asDouble(); // return value.value();
case BOOLEAN: // case BOOLEAN:
return value.asBoolean(); // return value.asBoolean();
case OPAQUE: // case OPAQUE:
return Base64.getDecoder().decode(value.asString()); // return Base64.getDecoder().decode(value.asString());
case STRING: // case STRING:
return value.asString(); // return value.asString();
case TIME: // case TIME:
return new Date(value.asLong()); // return new Date(value.asLong());
case OBJLNK: // case OBJLNK:
return ObjectLink.decodeFromString(value.asString()); // return ObjectLink.decodeFromString(value.asString());
case UNSIGNED_INTEGER: // case UNSIGNED_INTEGER:
return ULong.valueOf(value.asString()); // return ULong.valueOf(value.asString());
default: default:
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); throw new LwM2mNodeException(String.format("Type %s is not supported", type.name()));
} }
} }
private static JsonObject serialize(ResourceModel resourceModel) { private static JsonObject serialize(ResourceModel resourceModel) {
JsonObject o = Json.object(); JsonObject o = new JsonObject();
o.add("id", resourceModel.id); o.addProperty("id", resourceModel.id);
o.add("name", resourceModel.name); o.addProperty("name", resourceModel.name);
o.add("operations", resourceModel.operations.toString()); o.addProperty("operations", resourceModel.operations.toString());
o.add("multiple", resourceModel.multiple); o.addProperty("multiple", resourceModel.multiple);
o.add("mandatory", resourceModel.mandatory); o.addProperty("mandatory", resourceModel.mandatory);
o.add("type", resourceModel.type.toString()); o.addProperty("type", resourceModel.type.toString());
o.add("rangeEnumeration", resourceModel.rangeEnumeration); o.addProperty("rangeEnumeration", resourceModel.rangeEnumeration);
o.add("units", resourceModel.units); o.addProperty("units", resourceModel.units);
o.add("description", resourceModel.description); o.addProperty("description", resourceModel.description);
return o; return o;
} }
private static ResourceModel parseResourceModel(JsonObject o) { private static ResourceModel parseResourceModel(JsonObject o) {
Integer id = o.get("id").asInt(); Integer id = o.get("id").getAsInt();
String name = o.get("name").asString(); String name = o.get("name").getAsString();
ResourceModel.Operations operations = ResourceModel.Operations.valueOf(o.get("operations").asString()); ResourceModel.Operations operations = ResourceModel.Operations.valueOf(o.get("operations").getAsString());
Boolean multiple = o.get("multiple").asBoolean(); Boolean multiple = o.get("multiple").getAsBoolean();
Boolean mandatory = o.get("mandatory").asBoolean(); Boolean mandatory = o.get("mandatory").getAsBoolean();
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").asString()); ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").getAsString());
String rangeEnumeration = o.get("rangeEnumeration").asString(); String rangeEnumeration = o.get("rangeEnumeration").getAsString();
String units = o.get("units").asString(); String units = o.get("units").getAsString();
String description = o.get("description").asString(); String description = o.get("description").getAsString();
return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description); return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description);
} }
private static void addValue(JsonObject o, ResourceModel.Type type, Object value) { private static void addValue(JsonObject o, ResourceModel.Type type, Object value) {
switch (type) { switch (type) {
case INTEGER: case INTEGER:
o.add(VALUE, (Long) value); o.addProperty(VALUE, (Long) value);
break; break;
case FLOAT: case FLOAT:
o.add(VALUE, (Double) value); o.addProperty(VALUE, (Double) value);
break; break;
case BOOLEAN: case BOOLEAN:
o.add(VALUE, (Boolean) value); o.addProperty(VALUE, (Boolean) value);
break; break;
case OPAQUE: case OPAQUE:
o.add(VALUE, Base64.getEncoder().encodeToString((byte[]) value)); o.addProperty(VALUE, Base64.getEncoder().encodeToString((byte[]) value));
break; break;
case STRING: case STRING:
o.add(VALUE, (String) value); o.addProperty(VALUE, (String) value);
break; break;
case TIME: case TIME:
o.add(VALUE, ((Date) value).getTime()); o.addProperty(VALUE, ((Date) value).getTime());
break; break;
case OBJLNK: case OBJLNK:
o.add(VALUE, ((ObjectLink) value).encodeToString()); o.addProperty(VALUE, ((ObjectLink) value).encodeToString());
break; break;
case UNSIGNED_INTEGER: case UNSIGNED_INTEGER:
o.add(VALUE, value.toString()); o.addProperty(VALUE, value.toString());
break; break;
default: default:
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); throw new LwM2mNodeException(String.format("Type %s is not supported", type.name()));
@ -237,111 +230,111 @@ public class LwM2MClientSerDes {
@SneakyThrows @SneakyThrows
public static LwM2mClient deserialize(byte[] data) { public static LwM2mClient deserialize(byte[] data) {
JsonObject o = Json.parse(new String(data)).asObject(); // JsonObject o = new JsonObject(new String(data)));
LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").asString(), o.get("endpoint").asString()); // LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").getAsString(), o.get("endpoint").getAsString());
LwM2mClient lwM2mClient = new LwM2mClient("nodeId", "endpoint");
o.get("resources").asObject().forEach(entry -> { // o.get("resources").getAsJsonObject().forEach(entry -> {
JsonObject resource = entry.getValue().asObject(); // JsonObject resource = entry.getValue().asObject();
LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").asObject()); // LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").getAsJsonObject());
ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").asObject()); // ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").asObject());
ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel); // ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel);
lwM2mClient.getResources().put(entry.getName(), resourceValue); // lwM2mClient.getResources().put(entry.getName(), resourceValue);
}); // });
//
for (JsonObject.Member entry : o.get("sharedAttributes").asObject()) { // for (JsonObject.Member entry : o.get("sharedAttributes").asObject()) {
TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder(); // TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder();
JsonFormat.parser().merge(entry.getValue().asString(), builder); // JsonFormat.parser().merge(entry.getValue().getAsString(), builder);
lwM2mClient.getSharedAttributes().put(entry.getName(), builder.build()); // lwM2mClient.getSharedAttributes().put(entry.getName(), builder.build());
} // }
//
o.get("keyTsLatestMap").asObject().forEach(entry -> { // o.get("keyTsLatestMap").asObject().forEach(entry -> {
lwM2mClient.getKeyTsLatestMap().put(entry.getName(), new AtomicLong(entry.getValue().asLong())); // lwM2mClient.getKeyTsLatestMap().put(entry.getName(), new AtomicLong(entry.getValue().asLong()));
}); // });
//
lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").asString())); // lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").getAsString()));
//
Class<LwM2mClient> lwM2mClientClass = LwM2mClient.class; // Class<LwM2mClient> lwM2mClientClass = LwM2mClient.class;
//
JsonValue session = o.get("session"); // JsonValue session = o.get("session");
if (session != null) { // if (session != null) {
TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder(); // TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder();
JsonFormat.parser().merge(session.asString(), builder); // JsonFormat.parser().merge(session.asString(), builder);
//
Field sessionField = lwM2mClientClass.getDeclaredField("session"); // Field sessionField = lwM2mClientClass.getDeclaredField("session");
sessionField.setAccessible(true); // sessionField.setAccessible(true);
sessionField.set(lwM2mClient, builder.build()); // sessionField.set(lwM2mClient, builder.build());
} // }
//
JsonValue tenantId = o.get("tenantId"); // JsonValue tenantId = o.get("tenantId");
if (tenantId != null) { // if (tenantId != null) {
Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId"); // Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId");
tenantIdField.setAccessible(true); // tenantIdField.setAccessible(true);
tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.asString()))); // tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.asString())));
} // }
//
JsonValue deviceId = o.get("deviceId"); // JsonValue deviceId = o.get("deviceId");
if (tenantId != null) { // if (tenantId != null) {
Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId"); // Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId");
deviceIdField.setAccessible(true); // deviceIdField.setAccessible(true);
deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.asString())); // deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.asString()));
} // }
//
JsonValue profileId = o.get("profileId"); // JsonValue profileId = o.get("profileId");
if (tenantId != null) { // if (tenantId != null) {
Field profileIdField = lwM2mClientClass.getDeclaredField("profileId"); // Field profileIdField = lwM2mClientClass.getDeclaredField("profileId");
profileIdField.setAccessible(true); // profileIdField.setAccessible(true);
profileIdField.set(lwM2mClient, UUID.fromString(profileId.asString())); // profileIdField.set(lwM2mClient, UUID.fromString(profileId.asString()));
} // }
//
JsonValue powerMode = o.get("powerMode"); // JsonValue powerMode = o.get("powerMode");
if (powerMode != null) { // if (powerMode != null) {
Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode"); // Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode");
powerModeField.setAccessible(true); // powerModeField.setAccessible(true);
powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.asString())); // powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.asString()));
} // }
//
JsonValue edrxCycle = o.get("edrxCycle"); // JsonValue edrxCycle = o.get("edrxCycle");
if (edrxCycle != null) { // if (edrxCycle != null) {
Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle"); // Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle");
edrxCycleField.setAccessible(true); // edrxCycleField.setAccessible(true);
edrxCycleField.set(lwM2mClient, edrxCycle.asLong()); // edrxCycleField.set(lwM2mClient, edrxCycle.asLong());
} // }
//
JsonValue psmActivityTimer = o.get("psmActivityTimer"); // JsonValue psmActivityTimer = o.get("psmActivityTimer");
if (psmActivityTimer != null) { // if (psmActivityTimer != null) {
Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer"); // Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer");
psmActivityTimerField.setAccessible(true); // psmActivityTimerField.setAccessible(true);
psmActivityTimerField.set(lwM2mClient, psmActivityTimer.asLong()); // psmActivityTimerField.set(lwM2mClient, psmActivityTimer.asLong());
} // }
//
JsonValue pagingTransmissionWindow = o.get("pagingTransmissionWindow"); // JsonValue pagingTransmissionWindow = o.get("pagingTransmissionWindow");
if (pagingTransmissionWindow != null) { // if (pagingTransmissionWindow != null) {
Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow"); // Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow");
pagingTransmissionWindowField.setAccessible(true); // pagingTransmissionWindowField.setAccessible(true);
pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.asLong()); // pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.asLong());
} // }
//
JsonValue registration = o.get("registration"); // JsonValue registration = o.get("registration");
if (registration != null) { // if (registration != null) {
lwM2mClient.setRegistration(RegistrationSerDes.deserialize(registration.asObject())); // lwM2mClient.setRegistration(RegistrationSerDes.deserialize(registration.asObject()));
} // }
//
lwM2mClient.setAsleep(o.get("asleep").asBoolean()); // lwM2mClient.setAsleep(o.get("asleep").getAsBoolean());
//
Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime"); // Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime");
lastUplinkTimeField.setAccessible(true); // lastUplinkTimeField.setAccessible(true);
lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").asLong()); // lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").asLong());
//
Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink"); // Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink");
firstEdrxDownlinkField.setAccessible(true); // firstEdrxDownlinkField.setAccessible(true);
firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").asBoolean()); // firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").getAsBoolean());
//
lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").asInt()); // lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").asInt());
//
JsonValue lastSentRpcId = o.get("lastSentRpcId"); // JsonValue lastSentRpcId = o.get("lastSentRpcId");
if (lastSentRpcId != null) { // if (lastSentRpcId != null) {
lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.asString())); // lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.asString()));
} // }
return lwM2mClient; return lwM2mClient;
} }

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java

@ -356,7 +356,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl
*/ */
@Override @Override
public void onUpdateValueWithSendRequest(Registration registration, SendRequest sendRequest) { public void onUpdateValueWithSendRequest(Registration registration, SendRequest sendRequest) {
for(var entry : sendRequest.getNodes().entrySet()) { for(var entry : sendRequest.getTimestampedNodes().getNodes().entrySet()) {
LwM2mPath path = entry.getKey(); LwM2mPath path = entry.getKey();
LwM2mNode node = entry.getValue(); LwM2mNode node = entry.getValue();
LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
@ -777,7 +777,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl
this.updateResourcesValue(client, (LwM2mResource) v, k.toString(), Mode.REPLACE, code); this.updateResourcesValue(client, (LwM2mResource) v, k.toString(), Mode.REPLACE, code);
} else { } else {
LwM2mResourceInstance resourceInstance = (LwM2mResourceInstance) v; LwM2mResourceInstance resourceInstance = (LwM2mResourceInstance) v;
LwM2mMultipleResource multipleResource = new LwM2mMultipleResource(v.getId(), resourceInstance.getType(), resourceInstance); LwM2mMultipleResource multipleResource = new LwM2mMultipleResource(((LwM2mResourceInstance) v).getId(), resourceInstance.getType(), resourceInstance);
this.updateResourcesValue(client, multipleResource, k.toString(), Mode.REPLACE, code); this.updateResourcesValue(client, multipleResource, k.toString(), Mode.REPLACE, code);
} }
}); });

133
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java

@ -15,11 +15,8 @@
*/ */
package org.thingsboard.server.transport.lwm2m.utils; package org.thingsboard.server.transport.lwm2m.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectLoader;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
@ -30,8 +27,6 @@ import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -52,9 +47,7 @@ import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdate
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState;
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult; import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState; import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -62,13 +55,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION;
import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN;
import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN;
import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.eclipse.leshan.core.attributes.Attribute.STEP;
import static org.eclipse.leshan.core.model.ResourceModel.Type.BOOLEAN; import static org.eclipse.leshan.core.model.ResourceModel.Type.BOOLEAN;
import static org.eclipse.leshan.core.model.ResourceModel.Type.FLOAT; import static org.eclipse.leshan.core.model.ResourceModel.Type.FLOAT;
import static org.eclipse.leshan.core.model.ResourceModel.Type.INTEGER; import static org.eclipse.leshan.core.model.ResourceModel.Type.INTEGER;
@ -83,6 +69,7 @@ import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaU
import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID;
import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_STATE_ID;
@Slf4j @Slf4j
public class LwM2MTransportUtil { public class LwM2MTransportUtil {
@ -235,7 +222,7 @@ public class LwM2MTransportUtil {
} }
public static String convertObjectIdToVersionedId(String path, Registration registration) { public static String convertObjectIdToVersionedId(String path, Registration registration) {
String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId()); String ver = String.valueOf(registration.getSupportedObject().get(new LwM2mPath(path).getObjectId()));
ver = ver != null ? ver : TbLwM2mVersion.VERSION_1_0.getVersion().toString(); ver = ver != null ? ver : TbLwM2mVersion.VERSION_1_0.getVersion().toString();
try { try {
String[] keyArray = path.split(LWM2M_SEPARATOR_PATH); String[] keyArray = path.split(LWM2M_SEPARATOR_PATH);
@ -289,28 +276,28 @@ public class LwM2MTransportUtil {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st}; * Attribute [] attrs = {gt, st};
*/ */
public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, LwM2mUplinkMsgHandler serviceImpl) { // public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, LwM2mUplinkMsgHandler serviceImpl) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target)); // AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; // return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
} // }
private static Attribute[] createWriteAttributes(Object params, LwM2mUplinkMsgHandler serviceImpl, String target) { // private static Attribute[] createWriteAttributes(Object params, LwM2mUplinkMsgHandler serviceImpl, String target) {
List<Attribute> attributeLists = new ArrayList<>(); // List<Attribute> attributeLists = new ArrayList<>();
Map<String, Object> map = JacksonUtil.convertValue(params, new TypeReference<>() { // Map<String, Object> map = JacksonUtil.convertValue(params, new TypeReference<>() {
}); // });
map.forEach((k, v) -> { // map.forEach((k, v) -> {
if (StringUtils.trimToNull(v.toString()) != null) { // if (StringUtils.trimToNull(v.toString()) != null) {
Object attrValue = convertWriteAttributes(k, v, serviceImpl, target); // Object attrValue = convertWriteAttributes(k, v, serviceImpl, target);
if (attrValue != null) { // if (attrValue != null) {
Attribute attribute = createAttribute(k, attrValue); // Attribute attribute = createAttribute(k, attrValue);
if (attribute != null) { // if (attribute != null) {
attributeLists.add(new Attribute(k, attrValue)); // attributeLists.add(new Attribute(k, attrValue));
} // }
} // }
} // }
}); // });
return attributeLists.toArray(Attribute[]::new); // return attributeLists.toArray(Attribute[]::new);
} // }
/** /**
* "UNSIGNED_INTEGER": // Number -> Integer Example: * "UNSIGNED_INTEGER": // Number -> Integer Example:
@ -360,46 +347,50 @@ public class LwM2MTransportUtil {
public static Map<Integer, Object> convertMultiResourceValuesFromJson(JsonElement newValProto, ResourceModel.Type type, String versionedId) { public static Map<Integer, Object> convertMultiResourceValuesFromJson(JsonElement newValProto, ResourceModel.Type type, String versionedId) {
Map<Integer, Object> newValues = new HashMap<>(); Map<Integer, Object> newValues = new HashMap<>();
newValProto.getAsJsonObject().entrySet().forEach((obj) -> { newValProto.getAsJsonObject().entrySet().forEach((obj) -> {
newValues.put(Integer.valueOf(obj.getKey()), LwM2mValueConverterImpl.getInstance().convertValue(obj.getValue().getAsString(), newValues.put(Integer.valueOf(obj.getKey()), convertValueByTypeResource (obj.getValue().getAsString(), type, versionedId));
STRING, type, new LwM2mPath(fromVersionedIdToObjectId(versionedId))));
}); });
return newValues; return newValues;
} }
public static Object convertWriteAttributes(String type, Object value, LwM2mUplinkMsgHandler serviceImpl, String target) { public static Object convertValueByTypeResource (String value, ResourceModel.Type type, String versionedId) {
switch (type) { return LwM2mValueConverterImpl.getInstance().convertValue(value,
/** Integer [0:255]; */ STRING, type, new LwM2mPath(fromVersionedIdToObjectId(versionedId)));
case DIMENSION:
Long dim = (Long) serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
return dim >= 0 && dim <= 255 ? dim : null;
/**String;*/
case OBJECT_VERSION:
return serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), STRING, new LwM2mPath(target));
/**INTEGER */
case MINIMUM_PERIOD:
case MAXIMUM_PERIOD:
return serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
/**Float; */
case GREATER_THAN:
case LESSER_THAN:
case STEP:
if (value.getClass().getSimpleName().equals("String")) {
value = Double.valueOf((String) value);
}
return serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target));
default:
return null;
}
} }
private static Attribute createAttribute(String key, Object attrValue) { // public static Object convertWriteAttributes(String type, Object value, LwM2mUplinkMsgHandler serviceImpl, String target) {
try { // switch (type) {
return new Attribute(key, attrValue); // /** Integer [0:255]; */
} catch (Exception e) { // case DIMENSION:
log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage()); // Long dim = (Long) serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
return null; // return dim >= 0 && dim <= 255 ? dim : null;
} // /**String;*/
} // case OBJECT_VERSION:
// return serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), STRING, new LwM2mPath(target));
// /**INTEGER */
// case MINIMUM_PERIOD:
// case MAXIMUM_PERIOD:
// return serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
// /**Float; */
// case GREATER_THAN:
// case LESSER_THAN:
// case STEP:
// if (value.getClass().getSimpleName().equals("String")) {
// value = Double.valueOf((String) value);
// }
// return serviceImpl.getConverter().convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target));
// default:
// return null;
// }
// }
// private static Attribute createAttribute(String key, Object attrValue) {
// try {
// return new Attribute(key, attrValue);
// } catch (Exception e) {
// log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage());
// return null;
// }
// }
/** /**
* @param lwM2MClient - * @param lwM2MClient -

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

@ -327,10 +327,13 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) { if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) {
throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + "."); throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + ".");
} }
String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server" + " shortServerId: " + serverConfig.getShortServerId() + ":"; if (!serverConfig.isBootstrapServerIs() && (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534)) {
if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) { throw new DeviceCredentialsValidationException("LwM2M Server ShortServerId must not be less than 1 and more than 65534!");
throw new DeviceCredentialsValidationException(server + " ShortServerId must not be less than 1 and more than 65534!");
} }
if (serverConfig.isBootstrapServerIs() && !(serverConfig.getShortServerId() == null || serverConfig.getShortServerId() ==0)) {
throw new DeviceCredentialsValidationException("Bootstrap Server ShortServerId must be null or '0'!");
}
String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server";
if (!shortServerIds.add(serverConfig.getShortServerId())) { if (!shortServerIds.add(serverConfig.getShortServerId())) {
throw new DeviceCredentialsValidationException(server + " \"Short server Id\" value = " + serverConfig.getShortServerId() + ". This value must be a unique value for all servers!"); throw new DeviceCredentialsValidationException(server + " \"Short server Id\" value = " + serverConfig.getShortServerId() + ". This value must be a unique value for all servers!");
} }

10
pom.xml

@ -40,7 +40,7 @@
<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder> <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
<jakarta-annotation.version>2.1.1</jakarta-annotation.version> <jakarta-annotation.version>2.1.1</jakarta-annotation.version>
<jakarta.xml.bind-api.version>4.0.0</jakarta.xml.bind-api.version> <jakarta.xml.bind-api.version>4.0.0</jakarta.xml.bind-api.version>
<javax.xml.bind-api.version>2.3.1</javax.xml.bind-api.version> <javax.xml.bind-api.version>2.4.0-b180830.0359</javax.xml.bind-api.version>
<jaxb-runtime.version>4.0.2</jaxb-runtime.version> <jaxb-runtime.version>4.0.2</jaxb-runtime.version>
<spring-boot.version>3.1.0</spring-boot.version> <spring-boot.version>3.1.0</spring-boot.version>
<spring-data.version>3.1.0</spring-data.version> <spring-data.version>3.1.0</spring-data.version>
@ -67,13 +67,13 @@
<apache-httpclient.version>4.5.13</apache-httpclient.version> <apache-httpclient.version>4.5.13</apache-httpclient.version>
<apache-httpcore.version>4.4.14</apache-httpcore.version> <apache-httpcore.version>4.4.14</apache-httpcore.version>
<joda-time.version>2.8.1</joda-time.version> <joda-time.version>2.8.1</joda-time.version>
<jackson.version>2.13.4</jackson.version> <jackson.version>2.15.3</jackson.version>
<jackson-databind.version>2.13.4.2</jackson-databind.version> <jackson-databind.version>2.15.3</jackson-databind.version>
<fasterxml-classmate.version>1.3.4</fasterxml-classmate.version> <fasterxml-classmate.version>1.3.4</fasterxml-classmate.version>
<auth0-jwt.version>4.2.1</auth0-jwt.version> <auth0-jwt.version>4.2.1</auth0-jwt.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version> <json-schema-validator.version>2.2.6</json-schema-validator.version>
<californium.version>3.0.0</californium.version> <californium.version>3.9.1</californium.version>
<leshan.version>2.0.0-M5</leshan.version> <leshan.version>2.0.0-M14</leshan.version>
<gson.version>2.9.0</gson.version> <gson.version>2.9.0</gson.version>
<freemarker.version>2.3.30</freemarker.version> <freemarker.version>2.3.30</freemarker.version>
<mail.version>2.0.1</mail.version> <mail.version>2.0.1</mail.version>

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts

@ -25,7 +25,7 @@ export const ATTRIBUTE = 'attribute';
export const TELEMETRY = 'telemetry'; export const TELEMETRY = 'telemetry';
export const KEY_NAME = 'keyName'; export const KEY_NAME = 'keyName';
export const DEFAULT_ID_SERVER = 123; export const DEFAULT_ID_SERVER = 123;
export const DEFAULT_ID_BOOTSTRAP = 111; export const DEFAULT_ID_BOOTSTRAP = 0;
export const DEFAULT_LOCAL_HOST_NAME = 'localhost'; export const DEFAULT_LOCAL_HOST_NAME = 'localhost';
export const DEFAULT_PORT_SERVER_NO_SEC = 5685; export const DEFAULT_PORT_SERVER_NO_SEC = 5685;
export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5687; export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5687;

Loading…
Cancel
Save