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 org.apache.commons.io.IOUtils;
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.core.ResponseCode;
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_SECURE_PORT;
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.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
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 hostBs = "localhost";
public static final int shortServerId = 123;
public static final int shortServerIdBs = 111;
public static final Integer shortServerId = 123;
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 COAPS = "coaps://";
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 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_BS = noSecBootstap(URI_BS);
protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
" {\n" +
@ -235,8 +236,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
getWsClient().waitForReply();
getWsClient().registerWaitForUpdate();
createNewClient(security, coapConfig, false, endpoint, false, null);
awaitObserveReadAll(0, false, device.getId().getId().toString());
createNewClient(security, null, coapConfig, false, endpoint);
awaitObserveReadAll(0, device.getId().getId().toString());
String msg = getWsClient().waitForUpdate();
EntityDataUpdate update = JacksonUtil.fromString(msg, EntityDataUpdate.class);
@ -252,6 +253,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
int expectedMin = 5;
Assert.assertTrue(expectedMax >= Long.parseLong(tsValue.getValue()));
Assert.assertTrue(expectedMin <= Long.parseLong(tsValue.getValue()));
}
protected void createDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) throws Exception {
@ -300,14 +303,14 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
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();
lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint);
try (ServerSocket socket = new ServerSocket(0)) {
int clientPort = socket.getLocalPort();
lwM2MTestClient.init(security, coapConfig, clientPort, isRpc, isBootstrap, this.shortServerId, this.shortServerIdBs,
securityBs, this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest);
lwM2MTestClient.init(security, securityBs, coapConfig, clientPort, isRpc,
this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest);
}
}
@ -354,7 +357,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
private AbstractLwM2MBootstrapServerCredential getBootstrapServerCredentialNoSec(boolean isBootstrap) {
AbstractLwM2MBootstrapServerCredential bootstrapServerCredential = new NoSecLwM2MBootstrapServerCredential();
bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs : shortServerId);
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
bootstrapServerCredential.setPort(isBootstrap ? portBs : port);
@ -397,20 +400,18 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
.until(() -> leshanClient.getRegisteredServers().size() == 0);
}
protected void awaitObserveReadAll(int cntObserve, boolean isBootstrap, String deviceIdStr) throws Exception {
if (!isBootstrap) {
await("ObserveReadAll after start client: countObserve " + cntObserve)
.atMost(40, TimeUnit.SECONDS)
.until(() -> {
String actualResultReadAll = sendObserve("ObserveReadAll", null, deviceIdStr);
ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class);
Assert.assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText());
String actualValuesReadAll = rpcActualResultReadAll.get("value").asText();
log.warn("ObserveReadAll: [{}]", actualValuesReadAll);
int actualCntObserve = "[]".equals(actualValuesReadAll) ? 0 : actualValuesReadAll.split(",").length;
return cntObserve == actualCntObserve;
});
}
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception {
await("ObserveReadAll after start client: countObserve " + cntObserve)
.atMost(40, TimeUnit.SECONDS)
.until(() -> {
String actualResultReadAll = sendObserve("ObserveReadAll", null, deviceIdStr);
ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class);
Assert.assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText());
String actualValuesReadAll = rpcActualResultReadAll.get("value").asText();
log.warn("ObserveReadAll: [{}]", actualValuesReadAll);
int actualCntObserve = "[]".equals(actualValuesReadAll) ? 0 : actualValuesReadAll.split(",").length;
return cntObserve == actualCntObserve;
});
}
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 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.node.LwM2mResource;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse;
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);
@Override
public ReadResponse read(ServerIdentity identity, int resourceId) {
public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
if (!identity.isSystem())
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) {
@ -65,24 +65,24 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
}
@Override
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) {
String withParams = null;
if (params != null && params.length() != 0) {
withParams = " with params " + params;
}
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : "");
public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
String withArguments = "";
if (!arguments.isEmpty())
withArguments = " with arguments " + arguments;
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withArguments);
switch (resourceId) {
case 2:
startUpdating();
return ExecuteResponse.success();
default:
return super.execute(identity, resourceId, params);
return super.execute(identity, resourceId, arguments);
}
}
@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);
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.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.client.californium.LeshanClient;
import org.eclipse.leshan.client.californium.LeshanClientBuilder;
import org.eclipse.leshan.client.LeshanClient;
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.object.Security;
import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
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.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.model.InvalidDDFFileException;
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 java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.DEVICE;
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.SERVER;
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.LwM2MClientState;
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 String endpoint;
private LeshanClient leshanClient;
private Security lwm2mSecurity;
private Security lwm2mSecurityBs;
private Lwm2mServer lwm2mServer;
private Lwm2mServer lwm2mServerBs;
private SimpleLwM2MDevice lwM2MDevice;
private FwLwM2MDevice fwLwM2MDevice;
private SwLwM2MDevice swLwM2MDevice;
@ -112,8 +122,7 @@ public class LwM2MTestClient {
private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
private LwM2mClientContext clientContext;
public void init(Security security, Configuration coapConfig, int port, boolean isRpc, boolean isBootstrap,
int shortServerId, int shortServerIdBs, Security securityBs,
public void init(Security security, Security securityBs,Configuration coapConfig, int port, boolean isRpc,
LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler,
LwM2mClientContext clientContext) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", leshanClient);
@ -125,30 +134,36 @@ public class LwM2MTestClient {
}
LwM2mModel model = new StaticModel(models);
ObjectsInitializer initializer = new ObjectsInitializer(model);
if (securityBs == null) {
initializer.setInstancesForObject(SECURITY, this.lwm2mSecurity = security);
} else {
securityBs.setId(0);
security.setId(1);
LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{this.lwm2mSecurityBs = securityBs, this.lwm2mSecurity = security};
if (securityBs != null && security != null) {
// SECURITY
security.setId(serverId);
securityBs.setId(serverIdBs);
LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{securityBs, security};
initializer.setClassForObject(SECURITY, Security.class);
initializer.setInstancesForObject(SECURITY, instances);
}
if (isBootstrap) {
initializer.setInstancesForObject(SERVER, lwm2mServerBs = new Lwm2mServer(shortServerIdBs, 300));
// SERVER
Server lwm2mServer = new Server(shortServerId, 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 {
if (securityBs == null) {
initializer.setInstancesForObject(SERVER, lwm2mServer = new Lwm2mServer(shortServerId, 300));
} else {
lwm2mServerBs = new Lwm2mServer(shortServerIdBs, 300);
lwm2mServerBs.setId(0);
lwm2mServer = new Lwm2mServer(shortServerId, 300);
lwm2mServer.setId(1);
LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{lwm2mServerBs, lwm2mServer};
initializer.setClassForObject(SERVER, Server.class);
initializer.setInstancesForObject(SERVER, instances);
}
// SECURITY
initializer.setInstancesForObject(SECURITY, security);
// SERVER
Server lwm2mServer = new Server(shortServerId, 300);
lwm2mServer.setId(serverId);
initializer.setInstancesForObject(SERVER, lwm2mServer );
}
initializer.setInstancesForObject(DEVICE, lwM2MDevice = new SimpleLwM2MDevice(executor));
initializer.setInstancesForObject(FIRMWARE, fwLwM2MDevice = new FwLwM2MDevice());
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(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);
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();
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.setCommunicationPeriod(5000);
// new
/**
* Client use queue mode (not fully implemented).
*/
boolean queueMode = false;
engineFactory.setQueueMode(queueMode);
// Create client
LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
builder.setLocalAddress("0.0.0.0", port);
builder.setObjects(initializer.createAll());
builder.setCoapConfig(coapConfig);
builder.setDtlsConfig(dtlsConfig);
builder.setObjects(enablers);
builder.setEndpointsProviders(endpointsProvider.toArray(new LwM2mClientEndpointsProvider[endpointsProvider.size()]));
builder.setDataSenders(new ManualDataSender());
builder.setRegistrationEngineFactory(engineFactory);
boolean supportOldFormat = true;
if (supportOldFormat) {
builder.setDecoder(new DefaultLwM2mDecoder(supportOldFormat));
builder.setEncoder(new DefaultLwM2mEncoder(new LwM2mValueConverterImpl(), supportOldFormat));
}
builder.setRegistrationEngineFactory(engineFactory);
builder.setSharedExecutor(executor);
builder.setDecoder(new DefaultLwM2mDecoder(false));
builder.setEncoder(new DefaultLwM2mEncoder(new LwM2mValueConverterImpl(), false));
clientStates = new HashSet<>();
clientStates.add(ON_INIT);
leshanClient = builder.build();
LwM2mClientObserver observer = new LwM2mClientObserver() {
@Override
public void onBootstrapStarted(ServerIdentity bsserver, BootstrapRequest request) {
public void onBootstrapStarted(LwM2mServer bsserver, BootstrapRequest request) {
clientStates.add(ON_BOOTSTRAP_STARTED);
}
@Override
public void onBootstrapSuccess(ServerIdentity bsserver, BootstrapRequest request) {
public void onBootstrapSuccess(LwM2mServer bsserver, BootstrapRequest request) {
clientStates.add(ON_BOOTSTRAP_SUCCESS);
}
@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);
}
@Override
public void onBootstrapTimeout(ServerIdentity bsserver, BootstrapRequest request) {
public void onBootstrapTimeout(LwM2mServer bsserver, BootstrapRequest request) {
clientStates.add(ON_BOOTSTRAP_TIMEOUT);
}
@Override
public void onRegistrationStarted(ServerIdentity server, RegisterRequest request) {
public void onRegistrationStarted(LwM2mServer server, RegisterRequest request) {
clientStates.add(ON_REGISTRATION_STARTED);
}
@Override
public void onRegistrationSuccess(ServerIdentity server, RegisterRequest request, String registrationID) {
public void onRegistrationSuccess(LwM2mServer server, RegisterRequest request, String registrationID) {
clientStates.add(ON_REGISTRATION_SUCCESS);
}
@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);
}
@Override
public void onRegistrationTimeout(ServerIdentity server, RegisterRequest request) {
public void onRegistrationTimeout(LwM2mServer server, RegisterRequest request) {
clientStates.add(ON_REGISTRATION_TIMEOUT);
}
@Override
public void onUpdateStarted(ServerIdentity server, UpdateRequest request) {
public void onUpdateStarted(LwM2mServer server, UpdateRequest request) {
clientStates.add(ON_UPDATE_STARTED);
}
@Override
public void onUpdateSuccess(ServerIdentity server, UpdateRequest request) {
public void onUpdateSuccess(LwM2mServer server, UpdateRequest request) {
clientStates.add(ON_UPDATE_SUCCESS);
}
@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);
}
@Override
public void onUpdateTimeout(ServerIdentity server, UpdateRequest request) {
public void onUpdateTimeout(LwM2mServer server, UpdateRequest request) {
clientStates.add(ON_UPDATE_TIMEOUT);
}
@Override
public void onDeregistrationStarted(ServerIdentity server, DeregisterRequest request) {
public void onDeregistrationStarted(LwM2mServer server, DeregisterRequest request) {
clientStates.add(ON_DEREGISTRATION_STARTED);
}
@Override
public void onDeregistrationSuccess(ServerIdentity server, DeregisterRequest request) {
public void onDeregistrationSuccess(LwM2mServer server, DeregisterRequest request) {
clientStates.add(ON_DEREGISTRATION_SUCCESS);
}
@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);
}
@Override
public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) {
public void onDeregistrationTimeout(LwM2mServer server, DeregisterRequest request) {
clientStates.add(ON_DEREGISTRATION_TIMEOUT);
}
@ -270,6 +372,21 @@ public class LwM2MTestClient {
};
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) {
this.start(true);
}
@ -279,18 +396,6 @@ public class LwM2MTestClient {
if (leshanClient != null) {
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) {
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 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.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
@ -93,7 +92,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
}
@Override
public ReadResponse read(ServerIdentity identity, int resourceId) {
public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
try {
switch (resourceId) {
case 0:
@ -118,7 +117,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
}
@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);
switch (resourceId) {
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 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.response.ReadResponse;
@ -85,7 +84,7 @@ public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable {
}
@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);
switch (resourceId) {
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 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.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse;
@ -56,7 +56,7 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
}
@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);
switch (resourceId) {
case 5601:
@ -73,14 +73,14 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
}
@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);
switch (resourceId) {
case 5605:
resetMinMaxMeasuredValues();
return ExecuteResponse.success();
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 org.eclipse.leshan.client.resource.BaseInstanceEnabler;
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.ResourceModel.Type;
import org.eclipse.leshan.core.node.LwM2mResource;
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.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse;
@ -70,7 +70,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
}
@Override
public ReadResponse read(ServerIdentity identity, int resourceid) {
public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceid) {
if (!identity.isSystem())
LOG.debug("Read on Server resource /{}/{}/{}", getModel().id, getId(), resourceid);
@ -108,7 +108,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
}
@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())
log.debug("Write on Server resource /{}/{}/{}", getModel().id, getId(), resourceid);
@ -195,8 +195,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
}
}
@Override
public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceid, Arguments arguments) {
log.info("Execute on Server resource /{}/{}/{}", getModel().id, getId(), resourceid);
if (resourceid == 8) {
getLwM2mClient().triggerRegistrationUpdate(identity);
@ -210,7 +209,7 @@ public class Lwm2mServer extends BaseInstanceEnabler {
return ExecuteResponse.badRequest("probably no bootstrap server configured");
}
} 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 org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
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.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse;
@ -66,7 +66,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
@Override
public ReadResponse read(ServerIdentity identity, int resourceId) {
public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
if (!identity.isSystem())
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) {
@ -108,17 +108,16 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
}
@Override
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) {
String withParams = null;
if (params != null && params.length() != 0) {
withParams = " with params " + params;
}
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : "");
public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
String withArguments = "";
if (!arguments.isEmpty())
withArguments = " with arguments " + arguments;
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withArguments);
return ExecuteResponse.success();
}
@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);
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 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.node.LwM2mResource;
import org.eclipse.leshan.core.request.argument.Arguments;
import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse;
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);
@Override
public ReadResponse read(ServerIdentity identity, int resourceId) {
public ReadResponse read(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId) {
if (!identity.isSystem())
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) {
@ -63,12 +63,11 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
}
@Override
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) {
String withParams = null;
if (params != null && params.length() != 0) {
withParams = " with params " + params;
}
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : "");
public ExecuteResponse execute(org.eclipse.leshan.client.servers.LwM2mServer identity, int resourceId, Arguments arguments) {
String withArguments = "";
if (!arguments.isEmpty())
withArguments = " with arguments " + arguments;
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withArguments);
switch (resourceId) {
case 4:
@ -77,12 +76,12 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
case 6:
return ExecuteResponse.success();
default:
return super.execute(identity, resourceId, params);
return super.execute(identity, resourceId, arguments);
}
}
@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);
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);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(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);
awaitObserveReadAll(0, false, device.getId().getId().toString());
createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
awaitObserveReadAll(0, device.getId().getId().toString());
device.setFirmwareId(createFirmware().getId());
final Device savedDevice = doPost("/api/device", device, Device.class);
@ -124,8 +124,8 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(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);
awaitObserveReadAll(9, false, device.getId().getId().toString());
createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA5);
awaitObserveReadAll(9, device.getId().getId().toString());
device.setFirmwareId(createFirmware().getId());
final Device savedDevice = doPost("/api/device", device, Device.class);
@ -154,8 +154,8 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(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);
awaitObserveReadAll(9, false, device.getId().getId().toString());
createNewClient(SECURITY_NO_SEC, null, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA9);
awaitObserveReadAll(9, device.getId().getId().toString());
device.setSoftwareId(createSoftware().getId());
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;
import org.eclipse.leshan.core.link.LinkParser;
import org.eclipse.leshan.core.link.lwm2m.DefaultLwM2mLinkParser;
import org.junit.Before;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials;
@ -50,6 +52,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources;
@DaoSqlTest
public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final LinkParser linkParser = new DefaultLwM2mLinkParser();
protected String OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC;
protected String deviceId;
public Set expectedObjects;
@ -84,7 +87,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
private void initRpc () throws Exception {
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();
expectedObjectIdVers = ConcurrentHashMap.newKeySet();
expectedInstances = ConcurrentHashMap.newKeySet();
@ -147,7 +150,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
deviceId = device.getId().getId().toString();
lwM2MTestClient.start(true);
awaitObserveReadAll(2, false, device.getId().getId().toString());
// awaitObserveReadAll(2, true, device.getId().getId().toString());
}
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);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expectedObjectId = pathIdVerToObjectId((String) expectedPath);
String expectedObjectId = pathIdVerToObjectId(expectedPath);
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 actual = rpcActualResult.get("error").asText();
@ -118,7 +118,7 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expectedObjectId = pathIdVerToObjectId((String) expectedPath);
String expectedObjectId = pathIdVerToObjectId(expectedPath);
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 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.node.ObjectNode;
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.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
@ -52,12 +54,17 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration
Set actualObjects = ConcurrentHashMap.newKeySet();
Set actualInstances = ConcurrentHashMap.newKeySet();
rpcActualValue.forEach(node -> {
if (!node.get("uriReference").asText().equals("/")) {
LwM2mPath path = new LwM2mPath(node.get("uriReference").asText());
actualObjects.add("/" + path.getObjectId());
if (path.isObjectInstance()) {
actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId());
try {
Link[] parsedLink = linkParser.parseCoreLinkFormat(node.asText().getBytes());
if (!parsedLink[0].getUriReference().equals("/")) {
LwM2mPath path = new LwM2mPath(parsedLink[0].getUriReference());
actualObjects.add("/" + path.getObjectId());
if (path.isObjectInstance()) {
actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId());
}
}
} catch (LinkParseException e) {
throw new RuntimeException(e);
}
});
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
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);
ObjectNode rpcActualResultReadAll = JacksonUtil.fromString(actualResultReadAll, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResultReadAll.get("result").asText());
String actualValuesReadAll = rpcActualResultReadAll.get("value").asText();
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
public void testReadAllObjectsInClientById_Result_CONTENT_Value_IsLwM2mObject_IsInstances() throws Exception {
expectedObjectIdVers.forEach(expected -> {
try {
String actualResult = sendRPCById((String) expected);
String expectedObjectId = pathIdVerToObjectId((String) expected);
LwM2mPath expectedPath = new LwM2mPath(expectedObjectId);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources=";
if (expectedPath.getObjectId() == 2) {
expectedObjectInstances = "LwM2mObject [id=2, instances={}]";
try {
expectedObjectIdVers.forEach(expected -> {
try {
String actualResult = sendRPCById((String) expected);
String expectedObjectId = pathIdVerToObjectId((String) expected);
LwM2mPath expectedPath = new LwM2mPath(expectedObjectId);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources=";
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) {
e.printStackTrace();
}
});
});
} catch (Exception e2){
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 org.eclipse.leshan.core.ResponseCode;
import org.junit.Ignore;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
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 {
@Ignore
/**
* WriteAttributes {"id":"/3/0/14","attributes":{"pmax":100, "pmin":10}}
* if not implemented:
* {"result":"INTERNAL_SERVER_ERROR","error":"not implemented"}
* {"result":"BAD_REQUEST","error":"Content Format is mandatory"}
* if implemented:
* {"result":"CHANGED"}
*/
@ -42,8 +44,8 @@ public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MInte
String expectedValue = "{\"pmax\":100, \"pmin\":10}";
String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.INTERNAL_SERVER_ERROR.getName(), rpcActualResult.get("result").asText());
String expected = "not implemented";
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expected = "Content Format is mandatory";
String actual = rpcActualResult.get("error").asText();
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;
String expectedValue0 = "00ad45675600";
String expectedValue25 = "25ad45675600cdef";
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue);
String expectedValueResourcesObject19 = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValueResourcesObject19);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
String expectedPath0 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0;
@ -181,29 +181,37 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
/**
* 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"}
*/
@Test
public void testWriteCompositeValueSingleResourceResourceInstanceByIdKey_Result_CHANGED() throws Exception {
int resourceInstanceId2 = 2;
String expectedPath19_1_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId2;
String expectedValue19_1_0_2 = "00001234";
public void testWriteCompositeValueSingleResourceWithMultiResourceInstanceByIdKey_Result_CHANGED() throws Exception {
String expectedPath_19_0 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0;
int resourceInstanceId0 = 0;
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 expectedValue3_0_14 = "+04";
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15;
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 + "\"}";
String actualResult = sendCompositeRPC(nodes);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
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);
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));
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);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
@ -217,29 +225,36 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
/**
* multipleResource == error
* bad - cannot be used for value Json, only primitive: SingleResource, ResourceInstance (for Json: WriteUpdate, WriteReplace)
* WriteComposite {"nodes":{"/19/0/0":{"0":"abcd5678", "10":"abcd5678"}}}
* WriteComposite {"/19_1.1/0/0":{"0":"00ad45675600", "25":"25ad45675600cdef"}}
* {"result":"CHANGED"}
*/
@Test
public void testWriteCompositeValueSingleMultipleResourceByIdKey_Result_BAD_REQUEST_WriteComposite_operation_for_SingleResources_or_and_ResourceInstance() throws Exception {
String nodes = "{\"/19/0/0\":{\"0\":\"abcd5678\", \"10\":\"abcd5678\"}}";
public void testWriteCompositeValueSingleMultipleResourceOpaqueValueInputHexStringByIdKey_Result_CHANGED() throws Exception {
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);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String actualValues = rpcActualResult.get("error").asText();
String expectedNodes = nodes.replaceAll("\"", "").replaceAll(":", "=");
String expected = String.format("nodes: %s is not validate value. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", expectedNodes);
assertEquals(expected, actualValues);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
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
* 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"}}}
* {"result":"CHANGED"}
*/
@ -331,4 +346,4 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
String setRpcRequest = "{\"method\": \"WriteComposite\", \"params\": {\"nodes\":" + nodes + "}}";
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 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.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
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_CERT_ALIAS = "server";
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 serverX509CertBs; // serverBs certificate signed by rootCA
protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK
@ -171,7 +172,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
LwM2MClientState finishState) throws Exception {
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(type));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint));
this.basicTestConnection(noSecBootstap(URI_BS),
this.basicTestConnection(null , SECURITY_NO_SEC_BS,
deviceCredentials,
COAP_CONFIG_BS,
clientEndpoint,
@ -183,22 +184,24 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
false);
}
protected void basicTestConnection(Security security,
protected void basicTestConnection(Security security, Security securityBs,
LwM2MDeviceCredentials deviceCredentials,
Configuration coapConfig,
String endpoint,
Lwm2mDeviceProfileTransportConfiguration transportConfiguration,
String awaitAlias,
Set<LwM2MClientState> expectedStatuses,
boolean isBootstrap,
boolean isAwaitObserveReadAll,
LwM2MClientState finishState,
boolean isStartLw) throws Exception {
createDeviceProfile(transportConfiguration);
final Device device = createDevice(deviceCredentials, endpoint);
device.getId().getId().toString();
createNewClient(security, coapConfig, true, endpoint, isBootstrap, null);
createNewClient(security, securityBs, coapConfig, true, endpoint);
lwM2MTestClient.start(isStartLw);
awaitObserveReadAll(0, isBootstrap, device.getId().getId().toString());
if (isAwaitObserveReadAll) {
awaitObserveReadAll(0, device.getId().getId().toString());
}
await(awaitAlias)
.atMost(40, TimeUnit.SECONDS)
.until(() -> {
@ -220,34 +223,31 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint));
this.basicTestConnectionBootstrapRequestTrigger(
SECURITY_NO_SEC,
SECURITY_NO_SEC_BS,
deviceCredentials,
COAP_CONFIG,
clientEndpoint,
transportConfiguration,
awaitAlias,
expectedStatusesRegistrationLwm2mSuccess,
expectedStatusesRegistrationBsSuccess,
false,
SECURITY_NO_SEC_BS);
expectedStatusesRegistrationBsSuccess);
}
private void basicTestConnectionBootstrapRequestTrigger(Security security,
private void basicTestConnectionBootstrapRequestTrigger(Security security, Security securityBs,
LwM2MDeviceCredentials deviceCredentials,
Configuration coapConfig,
String endpoint,
Lwm2mDeviceProfileTransportConfiguration transportConfiguration,
String awaitAlias,
Set<LwM2MClientState> expectedStatusesLwm2m,
Set<LwM2MClientState> expectedStatusesBs,
boolean isBootstrap,
Security securityBs) throws Exception {
Set<LwM2MClientState> expectedStatusesBs) throws Exception {
createDeviceProfile(transportConfiguration);
final Device device = createDevice(deviceCredentials, endpoint);
String deviceIdStr = device.getId().getId().toString();
createNewClient(security, coapConfig, true, endpoint, isBootstrap, securityBs);
createNewClient(security, securityBs, coapConfig, true, endpoint);
lwM2MTestClient.start(true);
awaitObserveReadAll(0, isBootstrap, deviceIdStr);
awaitObserveReadAll(0, deviceIdStr);
await(awaitAlias)
.atMost(40, TimeUnit.SECONDS)
.until(() -> {
@ -263,7 +263,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatusesLwm2m));
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<>());
String actualResult = sendRPCSecurityExecuteById(executedPath, deviceIdStr, endpoint);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -337,7 +337,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
default:
throw new IllegalStateException("Unexpected value: " + mode);
}
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs : shortServerId);
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
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());
}
}

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.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.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH;
@ -39,7 +38,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT
// Bootstrap + Lwm2m
@Test
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)";
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);
}
@Test
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);
}
// Bs trigger
@Test
public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateTwoSectionAndLm2m_ConnectLwm2mSuccess() throws Exception {
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));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false);
this.basicTestConnection(security,
null,
deviceCredentials,
COAP_CONFIG,
clientEndpoint,
transportConfiguration,
"await on client state (Psk_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess,
false,
true,
ON_REGISTRATION_SUCCESS,
true);
}
@ -100,15 +101,15 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Hex.decodeHex(keyPsk.toCharArray()));
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false);
this.basicTestConnection(securityBs,
this.basicTestConnection(null, securityBs,
deviceCredentials,
COAP_CONFIG_BS,
clientEndpoint,
transportConfiguration,
"await on client state (PskBS two section)",
expectedStatusesRegistrationBsSuccess,
true,
false,
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();
clientCredentials.setEndpoint(clientEndpoint);
clientCredentials.setKey(Base64.encodeBase64String(certificate.getPublicKey().getEncoded()));
Security securityBs = rpk(SECURE_URI,
Security security = rpk(SECURE_URI,
shortServerId,
certificate.getPublicKey().getEncoded(),
privateKey.getEncoded(),
serverX509Cert.getPublicKey().getEncoded());
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, RPK, false);
this.basicTestConnection(securityBs,
this.basicTestConnection(security, null,
deviceCredentials,
COAP_CONFIG,
clientEndpoint,
transportConfiguration,
"await on client state (Rpk_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess,
false,
true,
ON_REGISTRATION_SUCCESS,
true);
}
@ -117,14 +117,14 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
serverX509CertBs.getPublicKey().getEncoded());
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, clientPrivateKeyFromCertTrust, certificate, RPK, false);
this.basicTestConnection(securityBs,
this.basicTestConnection(null, securityBs,
deviceCredentials,
COAP_CONFIG_BS,
clientEndpoint,
transportConfiguration,
"await on client state (RpkBS two section)",
expectedStatusesRegistrationBsSuccess,
true,
false,
ON_REGISTRATION_SUCCESS,
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));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security,
null,
deviceCredentials,
COAP_CONFIG,
clientEndpoint,
transportConfiguration,
"await on client state (X509_Trust_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess,
false,
true,
ON_REGISTRATION_SUCCESS,
true);
}
@ -118,13 +119,14 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security,
null,
deviceCredentials,
COAP_CONFIG_BS,
clientEndpoint,
transportConfiguration,
"await on client state (X509NoTrust two section)",
expectedStatusesRegistrationBsSuccess,
true,
false,
ON_REGISTRATION_SUCCESS,
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));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security,
null,
deviceCredentials,
COAP_CONFIG,
clientEndpoint,
transportConfiguration,
"await on client state (X509_Trust_Lwm2m)",
expectedStatusesRegistrationLwm2mSuccess,
false,
true,
ON_REGISTRATION_SUCCESS,
true);
}
@ -78,13 +79,14 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false);
this.basicTestConnection(security,
null,
deviceCredentials,
COAP_CONFIG_BS,
clientEndpoint,
transportConfiguration,
"await on client state (X509Trust two section)",
expectedStatusesRegistrationBsSuccess,
true,
false,
ON_REGISTRATION_SUCCESS,
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. " +
"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;
/** Security -> ObjectId = 0 'LWM2M Security' */
@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 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.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
@ -41,7 +39,7 @@ import java.util.Map;
@Slf4j
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)
throws InvalidDDFFileException, IOException {
@ -269,4 +267,4 @@ public class TbDDFFileParser {
}
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;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
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.DtlsConnectorConfig;
import org.eclipse.leshan.core.endpoint.Protocol;
import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
import org.eclipse.leshan.server.bootstrap.LeshanBootstrapServer;
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.thingsboard.server.common.transport.TransportService;
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.LwM2MTransportServerConfig;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.security.cert.X509Certificate;
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_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.RPK_OR_X509_CIPHER_SUITES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig;
@ -82,24 +85,121 @@ public class LwM2MTransportBootstrapService {
public LeshanBootstrapServer getLhBootstrapServer() {
LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
builder.setLocalAddress(bootstrapConfig.getHost(), bootstrapConfig.getPort());
builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort());
/* Create CoAP Config */
builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
// Create Californium Endpoints Provider:
// ------------------
// 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 */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
// endpointsBuilder.setConnector(dTLSConnector);
// endpointsBuilder.setConfiguration(serverCoapConfig);
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);
// endpointsBuilder.setLoggingTag(String.format("[%s]", "/" + "options.getUriPathString()"));
// endpointsBuilder.setEndpointContextMatcher(new Lwm2mEndpointContextMatcher());
/* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
/* Create credentials */
// 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 */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
@ -112,22 +212,19 @@ public class LwM2MTransportBootstrapService {
builder.setSessionManager(sessionManager);
/* Create BootstrapServer */
builder.setEndpointsProviders(endpointsBuilder.build());
return builder.build();
}
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) {
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
if (this.bootstrapConfig.getSslCredentials() != null) {
SslCredentials sslCredentials = this.bootstrapConfig.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else {
/* by default trust all */
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;
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.BootstrapFinishRequest;
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.server.bootstrap.BootstrapConfigStore;
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.server.client.LwM2MAuthException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -63,7 +67,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
* @param bsSecurityStore the {@link BootstrapSecurityStore} used by default {@link SecurityChecker}.
*/
public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore, TransportService transportService) {
this(bsSecurityStore, new SecurityChecker(), new LwM2MBootstrapConfigStoreTaskProvider(configStore),
this(bsSecurityStore, configStore, new SecurityChecker(), new LwM2MBootstrapConfigStoreTaskProvider(configStore),
new StandardBootstrapModelProvider());
this.transportService = transportService;
}
@ -74,9 +78,9 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
* @param bsSecurityStore the {@link BootstrapSecurityStore} used by {@link SecurityChecker}.
* @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) {
super(bsSecurityStore, securityChecker, tasksProvider, modelProvider);
super(bsSecurityStore, configStore);
this.bsSecurityStore = bsSecurityStore;
this.securityChecker = securityChecker;
this.tasksProvider = tasksProvider;
@ -84,23 +88,24 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
}
@Override
public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
boolean authorized = true;
public BootstrapSession begin(BootstrapRequest request, LwM2mPeer sender, URI endpointUsed) {
boolean authorized = sender.getIdentity().isSecure();
Iterator<SecurityInfo> securityInfos = null;
try {
try {
if (bsSecurityStore != null && securityChecker != null) {
if (clientIdentity.isPSK()) {
SecurityInfo securityInfo = bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity());
if (((IpPeer) sender).isPSK()) {
SecurityInfo securityInfo = bsSecurityStore.getByIdentity(((PskIdentity) sender.getIdentity()).getPskIdentity());
securityInfos = Collections.singletonList(securityInfo).iterator();
} else if (!clientIdentity.isX509()) {
}
else if (!((IpPeer) sender).isX509()) {
securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
}
authorized = this.checkSecurityInfo(request.getEndpointName(), clientIdentity, securityInfos);
authorized = this.checkSecurityInfo(request.getEndpointName(), sender, securityInfos);
}
} catch (LwM2MAuthException e) {
authorized = false;
}
DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized);
DefaultBootstrapSession session = new DefaultBootstrapSession(request, sender, authorized, null, endpointUsed);
if (authorized) {
try {
this.tasksProvider.put(session.getEndpoint());
@ -239,9 +244,9 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
transportService.log(((LwM2MBootstrapSecurityStore) bsSecurityStore).getSessionByEndpoint(endpointName), logMsg);
}
private boolean checkSecurityInfo(String endpoint, Identity clientIdentity, Iterator<SecurityInfo> securityInfos) {
if (clientIdentity.isX509()) {
return clientIdentity.getX509CommonName().equals(endpoint)
private boolean checkSecurityInfo(String endpoint, LwM2mPeer clientIdentity, Iterator<SecurityInfo> securityInfos) {
if (((IpPeer) clientIdentity).isX509()) {
return ((X509Identity)clientIdentity.getIdentity()).getX509CommonName().equals(endpoint)
& ((LwM2MBootstrapSecurityStore) bsSecurityStore).getBootstrapConfigByEndpoint(endpoint) != null;
} else {
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
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) {
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.
* "Short Server ID":
* - 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) {
log.info("Object after discover: [{}]", objectLinks);
@ -159,14 +160,14 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask
try {
LwM2mPath path = new LwM2mPath(link.getUriReference());
if (path.isObjectInstance()) {
if (link.getLinkParams().containsKey("ssid")) {
int serverId = Integer.parseInt(link.getLinkParams().get("ssid").getUnquoted());
if (link.getAttributes().get("ssid") != null) {
int serverId = Integer.parseInt(link.getAttributes().get("ssid").getCoreLinkValue());
if (!lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(serverId)) {
lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(serverId, path.getObjectInstanceId());
} else {
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 {
if (!this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(0)) {
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());
if (!path.isRoot() && path.getObjectId() < 3) {
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);
}
}

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;
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.EditableBootstrapConfigStore;
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) {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
this.addValueToStore(store, store.getEndpoint());
@ -210,4 +216,4 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
}
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()) {
BootstrapConfig.ServerConfig srvCfg = e.getValue();
// shortId checks
if (srvCfg.shortId == 0) {
throw new InvalidConfigurationException("short ID must not be 0");
}
// look for security entry
BootstrapConfig.ServerSecurity security = getSecurityEntry(config, srvCfg.shortId);
if (security == null) {
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;
}
}
}

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;
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.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.ConfigurationChecker;
import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
@ -39,8 +37,8 @@ public class LwM2MInMemoryBootstrapConfigStore extends InMemoryBootstrapConfigSt
private final Lock writeLock = readWriteLock.writeLock();
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);
}

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

@ -67,14 +67,14 @@ public class LwM2mCredentialsSecurityInfoValidator {
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
log.trace("Validated credentials: [{}] [{}]", credentialsId, msg);
log.info("Validated credentials: [{}] [{}]", credentialsId, msg);
resultSecurityStore[0] = createSecurityInfo(credentialsId, msg, keyValue);
latch.countDown();
}
@Override
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();
result.setEndpoint(credentialsId);
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.extern.slf4j.Slf4j;
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.server.registration.Registration;
import org.eclipse.leshan.server.security.Authorization;
import org.eclipse.leshan.server.security.Authorizer;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.eclipse.leshan.server.security.SecurityInfo;
@ -45,38 +47,30 @@ public class TbLwM2MAuthorizer implements Authorizer {
private final LwM2mClientContext clientContext;
@Override
public Registration isAuthorized(UplinkRequest<?> request, Registration registration, Identity senderIdentity) {
if (senderIdentity.isX509()) {
TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint());
if (sessionInfo != null) {
if (sessionInfo.getX509CommonName().endsWith(senderIdentity.getX509CommonName())) {
clientContext.registerClient(registration, sessionInfo.getCredentials());
public Authorization isAuthorized(UplinkRequest<?> request, Registration registration, LwM2mPeer sender) {
SecurityInfo expectedSecurityInfo = null;
if (securityStore != null) expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
if (securityChecker.checkSecurityInfo(registration.getEndpoint(), sender, expectedSecurityInfo)) {
if (sender.getIdentity() instanceof X509Identity) {
TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint());
if (sessionInfo != null) {
// X509 certificate is valid and matches endpoint.
return registration;
} else {
// X509 certificate is not valid.
return null;
clientContext.registerClient(registration, sessionInfo.getCredentials());
}
}
// 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 {
expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
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())) {
expectedSecurityInfo = null;
return Authorization.declined();
}
} catch (LwM2MAuthException e) {
log.info("Registration failed: FORBIDDEN, endpointId: [{}]", registration.getEndpoint());
return null;
return Authorization.declined();
}
if (securityChecker.checkSecurityInfo(registration.getEndpoint(), senderIdentity, expectedSecurityInfo)) {
return registration;
return Authorization.approved();
} else {
securityStore.remove(registration.getEndpoint(), registration.getId());
return null;
return Authorization.declined();
}
}
}

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 org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.eclipse.leshan.core.californium.identity.IdentityHandlerProvider;
@Slf4j
public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource {
public AbstractLwM2mTransportResource(String name) {
super(name);
super(name, new IdentityHandlerProvider());
}
@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;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
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.DtlsConnectorConfig;
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.DefaultLwM2mEncoder;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.LeshanServer;
import org.eclipse.leshan.server.LeshanServerBuilder;
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.stereotype.Component;
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.server.store.TbSecurityStore;
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 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_RETRANSMISSION_TIMEOUT;
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[] 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 LwM2MTransportServerConfig config;
private final OtaPackageDataCache otaPackageDataCache;
private final LwM2mUplinkMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore;
private final RegistrationStore registrationStore;
private final TbSecurityStore securityStore;
private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
private final TbLwM2MAuthorizer authorizer;
@ -88,7 +93,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
* "coap://host:port/{path}/{token}/{nameFile}"
*/
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.startLhServer();
}
@ -117,54 +122,128 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private LeshanServer getLhServer() {
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 )*/
builder.setObjectModelProvider(modelProvider);
/* Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore);
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 */
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());
dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, config.isRecommendedCiphers());
dtlsConfig.set(DTLS_RETRANSMISSION_TIMEOUT, config.getDtlsRetransmissionTimeout(), MILLISECONDS);
dtlsConfig.set(DTLS_ROLE, SERVER_ONLY);
// endpointsBuilder.setLoggingTag(String.format("[%s]", "/" + "options.getUriPathString()"));
// endpointsBuilder.setEndpointContextMatcher(new Lwm2mEndpointContextMatcher());
/* 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();
}
private void setServerWithCredentials(LeshanServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) {
private void setServerWithCredentials(LeshanServerBuilder builder) {
// private void setServerWithCredentials(LeshanServerBuilder builder) {
if (this.config.getSslCredentials() != null) {
SslCredentials sslCredentials = this.config.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
builder.setAuthorizer(authorizer);
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else {
/* by default trust all */
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 static Configuration getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) {
Configuration coapConfig = new Configuration();
public static Configuration getCoapConfig(Configuration coapConfig, Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) {
// Configuration coapConfig = new Configuration();
coapConfig.set(CoapConfig.COAP_PORT, serverPortNoSec);
coapConfig.set(CoapConfig.COAP_SECURE_PORT, serverSecurePort);
/**
@ -107,4 +107,4 @@ public class LwM2MNetworkConfig {
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;
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.Observation;
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 java.util.Collection;
import java.util.Map;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertObjectIdToVersionedId;
@ -128,10 +127,15 @@ public class LwM2mServerListener {
public final SendListener sendListener = new SendListener() {
@Override
public void dataReceived(Registration registration, Map<String, LwM2mNode> map, SendRequest sendRequest) {
public void dataReceived(Registration registration, TimestampedLwM2mNodes data, SendRequest request) {
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.Setter;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.LeshanServer;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
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;
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.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
@ -107,7 +107,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
@Override
public ObjectModel getObjectModel(int objectId) {
String version = registration.getSupportedVersion(objectId);
String version = String.valueOf(registration.getSupportedVersion(objectId));
if (version != null) {
return this.getObjectModelDynamic(objectId, version);
}
@ -116,10 +116,10 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
@Override
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());
for (Map.Entry<Integer, String> supportedObject : supportedObjects.entrySet()) {
ObjectModel objectModel = this.getObjectModelDynamic(supportedObject.getKey(), supportedObject.getValue());
for (Map.Entry<Integer, LwM2m.Version> supportedObject : supportedObjects.entrySet()) {
ObjectModel objectModel = this.getObjectModelDynamic(supportedObject.getKey(), String.valueOf(supportedObject.getValue()));
if (objectModel != null) {
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.ToString;
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.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
@ -271,7 +272,7 @@ public class LwM2mClient {
public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) {
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);
return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
@ -289,7 +290,7 @@ public class LwM2mClient {
public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) {
try {
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);
return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()) : null;
@ -370,12 +371,12 @@ public class LwM2mClient {
public String isValidObjectVersion(String 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) {
return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId());
} else {
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);
}
}
@ -432,16 +433,12 @@ public class LwM2mClient {
private static Set<ContentFormat> clientSupportContentFormat(Registration registration) {
Set<ContentFormat> contentFormats = new HashSet<>();
contentFormats.add(ContentFormat.DEFAULT);
LinkParamValue ct = Arrays.stream(registration.getObjectLinks())
Attribute ct = Arrays.stream(registration.getObjectLinks())
.filter(link -> link.getUriReference().equals("/"))
.findFirst()
.map(link -> link.getLinkParams().get("ct")).orElse(null);
.map(link -> link.getAttributes().get("ct")).orElse(null);
if (ct != null) {
Set<ContentFormat> codes = Stream.of(ct.getUnquoted().replaceAll("\"", "").split(" ", -1))
.map(String::trim)
.map(Integer::parseInt)
.map(ContentFormat::fromCode)
.collect(Collectors.toSet());
Set codes = Stream.of(ct.getValue()).collect(Collectors.toSet());
contentFormats.addAll(codes);
}
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;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.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.ObjectModel;
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.utils.LwM2mValueConverterImpl;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
@ -86,15 +87,16 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
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.STEP;
import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.GREATER_THAN;
import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.LESSER_THAN;
import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.MAXIMUM_PERIOD;
import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.MINIMUM_PERIOD;
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.OPAQUE;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody;
@ -148,8 +150,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override
public void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request,
DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback, ContentFormat compositeContentFormat) {
DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback) {
try {
ContentFormat compositeContentFormat = this.findFirstContentFormatForComposite(client.getClientSupportContentFormats());
ReadCompositeRequest downlink = new ReadCompositeRequest(compositeContentFormat, compositeContentFormat, request.getObjectIds());
sendCompositeRequest(client, downlink, this.config.getTimeout(), callback);
} catch (InvalidRequestException e) {
@ -193,8 +196,8 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
}
@Override
public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<Link>> callback) {
callback.onSuccess(request, Arrays.asList(client.getRegistration().getSortedObjectLinks()));
public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<String>> callback) {
callback.onSuccess(request, Arrays.stream(client.getRegistration().getSortedObjectLinks()).map(Link::toCoreLinkFormat).collect(Collectors.toList()));
}
@Override
@ -279,13 +282,13 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
throw new IllegalArgumentException("Attributes to write are not specified!");
}
ObjectAttributes params = request.getAttributes();
List<Attribute> attributes = new LinkedList<>();
List<LwM2mAttribute<?>> attributes = new LinkedList<>();
addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax());
addAttribute(attributes, MINIMUM_PERIOD, params.getPmin());
addAttribute(attributes, GREATER_THAN, params.getGt());
addAttribute(attributes, LESSER_THAN, params.getLt());
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);
} catch (InvalidRequestException e) {
callback.onValidationError(request.toString(), e.getMessage());
@ -338,9 +341,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override
public void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest rpcWriteCompositeRequest,
DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback, ContentFormat contentFormatComposite) {
DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback) {
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();
sendWriteCompositeRequest(client, downlink, this.config.getTimeout(), callback);
} 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) {
addAttribute(attributes, attributeName, value, null, null);
private static <T> void addAttribute(List<LwM2mAttribute<?>> attributes, LwM2mAttributeModel<T> attribute, T value) {
addAttribute(attributes, attribute, value, null, null);
}
private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value, Function<T, ?> converter) {
addAttribute(attributes, attributeName, value, null, converter);
private static <T> void addAttribute(List<LwM2mAttribute<?>> attributes, LwM2mAttributeModel<T> attribute, T value, Function<T, ?> 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))) {
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)) {
return ContentFormat.OPAQUE;
} 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 {
return getContentFormatForComplex(client);
@ -634,7 +639,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
if (LwM2m.LwM2mVersion.V1_0.equals(client.getRegistration().getLwM2mVersion())) {
return ContentFormat.TLV;
} 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) {
return result;
} else {
@ -644,16 +649,6 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
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) {
try {
return request != null ? request.toString() : "";
@ -662,4 +657,29 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
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;
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.DeleteRequest;
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 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);
@ -64,13 +62,13 @@ public interface LwM2mDownlinkMsgHandler {
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 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);

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.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
@ -75,6 +74,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
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;
@Slf4j
@ -155,22 +155,16 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
}
} else if (operationType.isComposite()) {
ContentFormat contentFormatComposite = this.getCompositeContentFormat(client);
if (contentFormatComposite != null) {
switch (operationType) {
case READ_COMPOSITE:
sendReadCompositeRequest(client, rpcRequest, contentFormatComposite);
sendReadCompositeRequest(client, rpcRequest);
break;
case WRITE_COMPOSITE:
sendWriteCompositeRequest(client, rpcRequest, contentFormatComposite);
sendWriteCompositeRequest(client, rpcRequest);
break;
default:
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 {
switch (operationType) {
case OBSERVE_CANCEL_ALL:
@ -202,12 +196,12 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
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);
TbLwM2MReadCompositeRequest request = TbLwM2MReadCompositeRequest.builder().versionedIds(versionedIds).timeout(clientContext.getRequestTimeout(client)).build();
var mainCallback = new TbLwM2MReadCompositeCallback(uplinkHandler, logService, client, versionedIds);
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) {
@ -243,7 +237,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
private void sendWriteAttributesRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
RpcWriteAttributesRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteAttributesRequest.class);
TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId)
TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId)
.attributes(requestBody.getAttributes())
.timeout(clientContext.getRequestTimeout(client)).build();
var mainCallback = new TbLwM2MWriteAttributesCallback(logService, client, versionedId);
@ -298,54 +292,89 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
* nodes.put("/1/0/2", 100);
* 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);
Map<String, Object> validNodes = validateNodes(client, rpcWriteCompositeRequest.getNodes());
Map<String, Object> validNodes = validateCompositesNodes(client, rpcWriteCompositeRequest.getNodes());
if (validNodes.size() > 0) {
rpcWriteCompositeRequest.setNodes(validNodes);
var mainCallback = new TbLwM2MWriteResponseCompositeCallback(uplinkHandler, logService, client, null);
var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback);
downlinkHandler.sendWriteCompositeRequest(client, rpcWriteCompositeRequest, rpcCallback, contentFormatComposite);
downlinkHandler.sendWriteCompositeRequest(client, rpcWriteCompositeRequest, rpcCallback);
} else {
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<>();
nodes.forEach((key, value) -> {
String versionedId;
String versionedId = null;
LwM2mPath path = null;
try {
LwM2mPath 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()));
}
path = new LwM2mPath(fromVersionedIdToObjectId(key));
} catch (Exception e) {
versionedId = clientContext.getObjectIdByKeyNameFromProfile(client, key);
}
// validate value. Must be only primitive, not JsonObject or JsonArray
try {
JsonElement element = JsonUtils.parse(value.toString());
if (!element.isJsonNull() && !element.isJsonPrimitive()) {
if (versionedId == null) {
if (path.isResourceInstance()) {
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. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString()));
}
else if (versionedId != null) {
newNodes.put(fromVersionedIdToObjectId(versionedId), value);
}
} catch (JsonSyntaxException jse) {
if (versionedId != null) {
newNodes.put(fromVersionedIdToObjectId(versionedId), value);
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes));
}
} else {
setValueToCompositeNodes(client, newNodes, nodes, versionedId, value.toString());
}
});
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) {
TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(clientContext.getRequestTimeout(client)).build();
var mainCallback = new TbLwM2MCancelObserveCallback(logService, client, versionedId);
@ -412,16 +441,4 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg 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) {
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;
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.SecurityInfo;
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
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
writeLock.lock();
try {
String identity = null;
if (tbSecurityInfo.getSecurityInfo() != null) {
identity = tbSecurityInfo.getSecurityInfo().getIdentity();
identity = tbSecurityInfo.getSecurityInfo().getPskIdentity();
if (identity != null) {
TbLwM2MSecurityInfo infoByIdentity = securityByIdentity.get(identity);
if (infoByIdentity != null && !tbSecurityInfo.getSecurityInfo().getEndpoint().equals(infoByIdentity.getEndpoint())) {
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);
if (previous != null && previous.getSecurityInfo() != null) {
String previousIdentity = previous.getSecurityInfo().getIdentity();
String previousIdentity = previous.getSecurityInfo().getPskIdentity();
if (previousIdentity != null && !previousIdentity.equals(identity)) {
securityByIdentity.remove(previousIdentity);
}
@ -116,8 +122,8 @@ public class TbInMemorySecurityStore implements TbEditableSecurityStore {
writeLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.remove(endpoint);
if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getIdentity() != null) {
securityByIdentity.remove(securityInfo.getSecurityInfo().getIdentity());
if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getPskIdentity() != null) {
securityByIdentity.remove(securityInfo.getSecurityInfo().getPskIdentity());
}
} finally {
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;
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.elements.EndpointContext;
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.californium.ObserveUtil;
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.request.Identity;
import org.eclipse.leshan.core.peer.LwM2mIdentity;
import org.eclipse.leshan.core.util.NamedThreadFactory;
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.serialization.IdentitySerDes;
import org.eclipse.leshan.server.redis.serialization.ObservationSerDes;
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
import org.eclipse.leshan.server.registration.Deregistration;
import org.eclipse.leshan.server.registration.ExpirationListener;
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.UpdatedRegistration;
import org.slf4j.Logger;
@ -63,7 +65,7 @@ import java.util.concurrent.locks.Lock;
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). */
public static final long DEFAULT_CLEAN_PERIOD = 60;
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
// (expiration date, Endpoint)
private final RegistrationSerDes registrationSerDes = new RegistrationSerDes();
private final ObservationSerDes observationSerDes = new ObservationSerDes(new UdpDataParser(), new UdpDataSerializer());
private final RedisConnectionFactory connectionFactory;
// 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));
byte[] addr_idx = toRegAddrKey(registration.getSocketAddress());
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));
// Add or update expiration
@ -173,7 +177,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) {
removeAddrIndex(connection, oldRegistration);
}
if (!oldRegistration.getIdentity().equals(registration.getIdentity())) {
if (!oldRegistration.getClientTransportData().getIdentity().equals(registration.getClientTransportData().getIdentity())) {
removeIdentityIndex(connection, oldRegistration);
}
// remove old observation
@ -231,7 +235,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
removeAddrIndex(connection, r);
}
if (!r.getIdentity().equals(updatedRegistration.getIdentity())) {
if (!r.getClientTransportData().getIdentity().equals(updatedRegistration.getClientTransportData().getIdentity())) {
removeIdentityIndex(connection, r);
}
@ -281,7 +285,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
@Override
public Registration getRegistrationByIdentity(Identity identity) {
public Registration getRegistrationByIdentity(LwM2mIdentity identity) {
Validate.notNull(identity);
try (var connection = connectionFactory.getConnection()) {
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) {
// fetch the client ep by registration ID index
byte[] ep = connection.get(toRegIdKey(registrationId));
@ -371,7 +395,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
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
@ -410,8 +434,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort());
}
private byte[] toRegIdentityKey(Identity identity) {
return toKey(REG_EP_IDENTITY, IdentitySerDes.serialize(identity).toString());
private byte[] toRegIdentityKey(LwM2mIdentity identity) {
return toKey(REG_EP_IDENTITY, identity.toString());
}
private byte[] toEndpointKey(String endpoint) {
@ -423,11 +447,11 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
private byte[] serializeReg(Registration registration) {
return RegistrationSerDes.bSerialize(registration);
return registrationSerDes.bSerialize(registration);
}
private Registration deserializeReg(byte[] data) {
return RegistrationSerDes.deserialize(data);
return registrationSerDes.deserialize(data);
}
/* *************** 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
* org.eclipse.californium.core.observe.ObservationStore#add method)
*/
@Override
public Collection<Observation> addObservation(String registrationId, Observation observation) {
List<Observation> removed = new ArrayList<>();
try (var connection = connectionFactory.getConnection()) {
@ -458,9 +482,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
for (Observation obs : getObservations(connection, registrationId)) {
//TODO: should be able to use CompositeObservation
if (((SingleObservation)observation).getPath().equals(((SingleObservation)obs).getPath())
&& !Arrays.equals(observation.getId(), obs.getId())) {
&& !observation.getId().equals(obs.getId())) {
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;
}
@Override
public Observation removeObservation(String registrationId, byte[] observationId) {
try (var connection = connectionFactory.getConnection()) {
@ -490,7 +513,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
lock = redisLock.obtain(lockKey);
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())) {
unsafeRemoveObservation(connection, registrationId, observationId);
return observation;
@ -505,9 +529,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
}
@Override
public Observation getObservation(String registrationId, byte[] observationId) {
return build(get(new Token(observationId)));
// return build(get(new Token(observationId)));
return build(null);
}
@Override
@ -554,13 +578,11 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
/* *************** Californium ObservationStore API **************** */
@Override
public org.eclipse.californium.core.observe.Observation putIfAbsent(Token token,
org.eclipse.californium.core.observe.Observation obs) throws ObservationStoreException {
return add(obs, true);
}
@Override
public org.eclipse.californium.core.observe.Observation put(Token token,
org.eclipse.californium.core.observe.Observation obs) throws ObservationStoreException {
return add(obs, false);
@ -588,7 +610,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (previousValue == null || previousValue.length == 0) {
connection.set(key, serializeObs);
} else {
return deserializeObs(previousValue);
// return deserializeObs(previousValue);
return null;
}
} else {
previousValue = connection.getSet(key, serializeObs);
@ -599,7 +622,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
// log any collisions
if (previousValue != null && previousValue.length != 0) {
previousObservation = deserializeObs(previousValue);
// previousObservation = deserializeObs(previousValue);
previousObservation = null;
LOG.warn(
"Token collision ? observation from request [{}] will be replaced by observation from request [{}] ",
previousObservation.getRequest(), obs.getRequest());
@ -613,7 +637,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return previousObservation;
}
@Override
public void remove(Token token) {
try (var connection = connectionFactory.getConnection()) {
byte[] tokenKey = toKey(OBS_TKN, token.getBytes());
@ -623,7 +646,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (serializedObs == null)
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);
Registration registration = getRegistration(connection, registrationId);
if (registration == null) {
@ -649,14 +673,14 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
@Override
public org.eclipse.californium.core.observe.Observation get(Token token) {
try (var connection = connectionFactory.getConnection()) {
byte[] obs = connection.get(toKey(OBS_TKN, token.getBytes()));
if (obs == null) {
return null;
} else {
return deserializeObs(obs);
// return deserializeObs(obs);
return null;
}
}
}
@ -699,24 +723,26 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return removed;
}
@Override
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.
}
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) {
return ObservationSerDes.deserialize(data);
private Observation deserializeObs(byte[] 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)
return null;
return ObserveUtil.createLwM2mObservation(cfObs.getRequest());
// String serializedObservation = observationSerDes.serialize(cfObs);
// return ObserveUtil.createLwM2mObservation(cfObs, serializedObservation);
return null;
}
/* *************** Expiration handling **************** */
@ -787,7 +813,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
expirationListener = listener;
}
@Override
public void setExecutor(ScheduledExecutorService 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;
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.SecurityInfo;
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
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
SecurityInfo info = tbSecurityInfo.getSecurityInfo();
@ -94,21 +100,21 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(tbSecurityInfo.getEndpoint());
lock.lock();
if (info != null && info.getIdentity() != null) {
byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
if (info != null && info.getPskIdentity() != null) {
byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getPskIdentity().getBytes());
if (oldEndpointBytes != null) {
String oldEndpoint = new String(oldEndpointBytes);
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);
if (previousData != null && info != null) {
String previousIdentity = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(previousData)).getSecurityInfo().getIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
String previousIdentity = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(previousData)).getSecurityInfo().getPskIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getPskIdentity())) {
connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
}
}
@ -147,8 +153,8 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data != null && data.length > 0) {
SecurityInfo info = ((TbLwM2MSecurityInfo) JavaSerDesUtil.decode(data)).getSecurityInfo();
if (info != null && info.getIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
if (info != null && info.getPskIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getPskIdentity().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 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.SecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
@ -60,7 +61,7 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore {
if (securityInfo == null) {
securityInfo = fetchAndPutSecurityInfo(endpoint);
} 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())) {
return null;
}
@ -81,6 +82,11 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore {
return securityInfo;
}
@Override
public SecurityInfo getByOscoreIdentity(OscoreIdentity oscoreIdentity) {
return null;
}
public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) {
TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT);
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;
import lombok.RequiredArgsConstructor;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore;
import org.eclipse.leshan.server.registration.InMemoryRegistrationStore;
import org.eclipse.leshan.server.registration.RegistrationStore;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Component;
@ -38,7 +38,7 @@ public class TbLwM2mStoreFactory {
private final LwM2mCredentialsSecurityInfoValidator validator;
@Bean
private CaliforniumRegistrationStore registrationStore() {
private RegistrationStore registrationStore() {
return redisConfiguration.isPresent() ?
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;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.gson.JsonObject;
import com.google.protobuf.util.JsonFormat;
import lombok.SneakyThrows;
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.LwM2mSingleResource;
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.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.transport.lwm2m.server.client.LwM2MClientState;
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.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
public class LwM2MClientSerDes {
public static final String VALUE = "value";
@SneakyThrows
public static byte[] serialize(LwM2mClient client) {
JsonObject o = Json.object();
o.add("nodeId", client.getNodeId());
o.add("endpoint", client.getEndpoint());
JsonObject o = new JsonObject();
o.addProperty("nodeId", "client.getNodeId()");
o.addProperty("endpoint", client.getEndpoint());
JsonObject resources = Json.object();
JsonObject resources = new JsonObject();
client.getResources().forEach((k, v) -> {
JsonObject resourceValue = Json.object();
JsonObject resourceValue = new JsonObject();
resourceValue.add("lwM2mResource", serialize(v.getLwM2mResource()));
resourceValue.add("resourceModel", serialize(v.getResourceModel()));
resources.add(k, resourceValue);
});
o.add("resources", resources);
JsonObject sharedAttributes = Json.object();
JsonObject sharedAttributes = new JsonObject();
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);
JsonObject keyTsLatestMap = Json.object();
JsonObject keyTsLatestMap = new JsonObject();
client.getKeyTsLatestMap().forEach((k, v) -> {
keyTsLatestMap.add(k, v.get());
keyTsLatestMap.addProperty(k, v.get());
});
o.add("keyTsLatestMap", keyTsLatestMap);
o.add("state", client.getState().toString());
o.addProperty("state", client.getState().toString());
if (client.getSession() != null) {
o.add("session", JsonFormat.printer().print(client.getSession()));
o.addProperty("session", JsonFormat.printer().print(client.getSession()));
}
if (client.getTenantId() != null) {
o.add("tenantId", client.getTenantId().toString());
o.addProperty("tenantId", client.getTenantId().toString());
}
if (client.getDeviceId() != null) {
o.add("deviceId", client.getDeviceId().toString());
o.addProperty("deviceId", client.getDeviceId().toString());
}
if (client.getProfileId() != null) {
o.add("profileId", client.getProfileId().toString());
o.addProperty("profileId", client.getProfileId().toString());
}
if (client.getPowerMode() != null) {
o.add("powerMode", client.getPowerMode().toString());
o.addProperty("powerMode", client.getPowerMode().toString());
}
if (client.getEdrxCycle() != null) {
o.add("edrxCycle", client.getEdrxCycle());
o.addProperty("edrxCycle", client.getEdrxCycle());
}
if (client.getPsmActivityTimer() != null) {
o.add("psmActivityTimer", client.getPsmActivityTimer());
o.addProperty("psmActivityTimer", client.getPsmActivityTimer());
}
if (client.getPagingTransmissionWindow() != null) {
o.add("pagingTransmissionWindow", client.getPagingTransmissionWindow());
o.addProperty("pagingTransmissionWindow", client.getPagingTransmissionWindow());
}
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.add("lastUplinkTime", client.getLastUplinkTime());
o.addProperty("asleep", client.isAsleep());
o.addProperty("lastUplinkTime", client.getLastUplinkTime());
Field firstEdrxDownlink = LwM2mClient.class.getDeclaredField("firstEdrxDownlink");
firstEdrxDownlink.setAccessible(true);
o.add("firstEdrxDownlink", (boolean) firstEdrxDownlink.get(client));
o.add("retryAttempts", client.getRetryAttempts().get());
o.addProperty("firstEdrxDownlink", (boolean) firstEdrxDownlink.get(client));
o.addProperty("retryAttempts", client.getRetryAttempts().get());
if (client.getLastSentRpcId() != null) {
o.add("lastSentRpcId", client.getLastSentRpcId().toString());
o.addProperty("lastSentRpcId", client.getLastSentRpcId().toString());
}
return o.toString().getBytes();
}
private static JsonObject serialize(LwM2mResource resource) {
JsonObject o = Json.object();
o.add("id", resource.getId());
o.add("type", resource.getType().toString());
JsonObject o = new JsonObject();
o.addProperty("id", resource.getId());
o.addProperty("type", resource.getType().toString());
if (resource.isMultiInstances()) {
o.add("multiInstances", true);
JsonObject instances = Json.object();
o.addProperty("multiInstances", true);
JsonObject instances = new JsonObject();
resource.getInstances().forEach((id, in) -> {
JsonObject instance = Json.object();
instance.add("id", in.getId());
JsonObject instance = new JsonObject();
instance.addProperty("id", in.getId());
addValue(instance, in.getType(), in.getValue());
instances.add(id.toString(), instance);
});
o.add("instances", instances);
} else {
o.add("multiInstances", false);
o.addProperty("multiInstances", false);
addValue(o, resource.getType(), resource.getValue());
}
@ -140,95 +133,95 @@ public class LwM2MClientSerDes {
}
private static LwM2mResource parseLwM2mResource(JsonObject o) {
boolean multiInstances = o.get("multiInstances").asBoolean();
int id = o.get("id").asInt();
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").asString());
boolean multiInstances = o.get("multiInstances").getAsBoolean();
int id = o.get("id").getAsInt();
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").getAsString());
if (multiInstances) {
Map<Integer, Object> instances = new HashMap<>();
o.get("instances").asObject().forEach(entry -> {
instances.put(Integer.valueOf(entry.getName()), parseValue(type, entry.getValue()));
o.get("instances").getAsJsonArray().forEach(entry -> {
// instances.put(Integer.valueOf(entry.getAsJsonObject().), parseValue(type, entry.getValue()));
});
return LwM2mMultipleResource.newResource(id, instances, type);
} 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) {
switch (type) {
case INTEGER:
return value.asLong();
case FLOAT:
return value.asDouble();
case BOOLEAN:
return value.asBoolean();
case OPAQUE:
return Base64.getDecoder().decode(value.asString());
case STRING:
return value.asString();
case TIME:
return new Date(value.asLong());
case OBJLNK:
return ObjectLink.decodeFromString(value.asString());
case UNSIGNED_INTEGER:
return ULong.valueOf(value.asString());
return value.value();
// case FLOAT:
// return value.value();
// case BOOLEAN:
// return value.asBoolean();
// case OPAQUE:
// return Base64.getDecoder().decode(value.asString());
// case STRING:
// return value.asString();
// case TIME:
// return new Date(value.asLong());
// case OBJLNK:
// return ObjectLink.decodeFromString(value.asString());
// case UNSIGNED_INTEGER:
// return ULong.valueOf(value.asString());
default:
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name()));
}
}
private static JsonObject serialize(ResourceModel resourceModel) {
JsonObject o = Json.object();
o.add("id", resourceModel.id);
o.add("name", resourceModel.name);
o.add("operations", resourceModel.operations.toString());
o.add("multiple", resourceModel.multiple);
o.add("mandatory", resourceModel.mandatory);
o.add("type", resourceModel.type.toString());
o.add("rangeEnumeration", resourceModel.rangeEnumeration);
o.add("units", resourceModel.units);
o.add("description", resourceModel.description);
JsonObject o = new JsonObject();
o.addProperty("id", resourceModel.id);
o.addProperty("name", resourceModel.name);
o.addProperty("operations", resourceModel.operations.toString());
o.addProperty("multiple", resourceModel.multiple);
o.addProperty("mandatory", resourceModel.mandatory);
o.addProperty("type", resourceModel.type.toString());
o.addProperty("rangeEnumeration", resourceModel.rangeEnumeration);
o.addProperty("units", resourceModel.units);
o.addProperty("description", resourceModel.description);
return o;
}
private static ResourceModel parseResourceModel(JsonObject o) {
Integer id = o.get("id").asInt();
String name = o.get("name").asString();
ResourceModel.Operations operations = ResourceModel.Operations.valueOf(o.get("operations").asString());
Boolean multiple = o.get("multiple").asBoolean();
Boolean mandatory = o.get("mandatory").asBoolean();
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").asString());
String rangeEnumeration = o.get("rangeEnumeration").asString();
String units = o.get("units").asString();
String description = o.get("description").asString();
Integer id = o.get("id").getAsInt();
String name = o.get("name").getAsString();
ResourceModel.Operations operations = ResourceModel.Operations.valueOf(o.get("operations").getAsString());
Boolean multiple = o.get("multiple").getAsBoolean();
Boolean mandatory = o.get("mandatory").getAsBoolean();
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").getAsString());
String rangeEnumeration = o.get("rangeEnumeration").getAsString();
String units = o.get("units").getAsString();
String description = o.get("description").getAsString();
return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description);
}
private static void addValue(JsonObject o, ResourceModel.Type type, Object value) {
switch (type) {
case INTEGER:
o.add(VALUE, (Long) value);
o.addProperty(VALUE, (Long) value);
break;
case FLOAT:
o.add(VALUE, (Double) value);
o.addProperty(VALUE, (Double) value);
break;
case BOOLEAN:
o.add(VALUE, (Boolean) value);
o.addProperty(VALUE, (Boolean) value);
break;
case OPAQUE:
o.add(VALUE, Base64.getEncoder().encodeToString((byte[]) value));
o.addProperty(VALUE, Base64.getEncoder().encodeToString((byte[]) value));
break;
case STRING:
o.add(VALUE, (String) value);
o.addProperty(VALUE, (String) value);
break;
case TIME:
o.add(VALUE, ((Date) value).getTime());
o.addProperty(VALUE, ((Date) value).getTime());
break;
case OBJLNK:
o.add(VALUE, ((ObjectLink) value).encodeToString());
o.addProperty(VALUE, ((ObjectLink) value).encodeToString());
break;
case UNSIGNED_INTEGER:
o.add(VALUE, value.toString());
o.addProperty(VALUE, value.toString());
break;
default:
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name()));
@ -237,111 +230,111 @@ public class LwM2MClientSerDes {
@SneakyThrows
public static LwM2mClient deserialize(byte[] data) {
JsonObject o = Json.parse(new String(data)).asObject();
LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").asString(), o.get("endpoint").asString());
o.get("resources").asObject().forEach(entry -> {
JsonObject resource = entry.getValue().asObject();
LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").asObject());
ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").asObject());
ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel);
lwM2mClient.getResources().put(entry.getName(), resourceValue);
});
for (JsonObject.Member entry : o.get("sharedAttributes").asObject()) {
TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder();
JsonFormat.parser().merge(entry.getValue().asString(), builder);
lwM2mClient.getSharedAttributes().put(entry.getName(), builder.build());
}
o.get("keyTsLatestMap").asObject().forEach(entry -> {
lwM2mClient.getKeyTsLatestMap().put(entry.getName(), new AtomicLong(entry.getValue().asLong()));
});
lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").asString()));
Class<LwM2mClient> lwM2mClientClass = LwM2mClient.class;
JsonValue session = o.get("session");
if (session != null) {
TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder();
JsonFormat.parser().merge(session.asString(), builder);
Field sessionField = lwM2mClientClass.getDeclaredField("session");
sessionField.setAccessible(true);
sessionField.set(lwM2mClient, builder.build());
}
JsonValue tenantId = o.get("tenantId");
if (tenantId != null) {
Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId");
tenantIdField.setAccessible(true);
tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.asString())));
}
JsonValue deviceId = o.get("deviceId");
if (tenantId != null) {
Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId");
deviceIdField.setAccessible(true);
deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.asString()));
}
JsonValue profileId = o.get("profileId");
if (tenantId != null) {
Field profileIdField = lwM2mClientClass.getDeclaredField("profileId");
profileIdField.setAccessible(true);
profileIdField.set(lwM2mClient, UUID.fromString(profileId.asString()));
}
JsonValue powerMode = o.get("powerMode");
if (powerMode != null) {
Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode");
powerModeField.setAccessible(true);
powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.asString()));
}
JsonValue edrxCycle = o.get("edrxCycle");
if (edrxCycle != null) {
Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle");
edrxCycleField.setAccessible(true);
edrxCycleField.set(lwM2mClient, edrxCycle.asLong());
}
JsonValue psmActivityTimer = o.get("psmActivityTimer");
if (psmActivityTimer != null) {
Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer");
psmActivityTimerField.setAccessible(true);
psmActivityTimerField.set(lwM2mClient, psmActivityTimer.asLong());
}
JsonValue pagingTransmissionWindow = o.get("pagingTransmissionWindow");
if (pagingTransmissionWindow != null) {
Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow");
pagingTransmissionWindowField.setAccessible(true);
pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.asLong());
}
JsonValue registration = o.get("registration");
if (registration != null) {
lwM2mClient.setRegistration(RegistrationSerDes.deserialize(registration.asObject()));
}
lwM2mClient.setAsleep(o.get("asleep").asBoolean());
Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime");
lastUplinkTimeField.setAccessible(true);
lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").asLong());
Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink");
firstEdrxDownlinkField.setAccessible(true);
firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").asBoolean());
lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").asInt());
JsonValue lastSentRpcId = o.get("lastSentRpcId");
if (lastSentRpcId != null) {
lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.asString()));
}
// JsonObject o = new JsonObject(new String(data)));
// LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").getAsString(), o.get("endpoint").getAsString());
LwM2mClient lwM2mClient = new LwM2mClient("nodeId", "endpoint");
// o.get("resources").getAsJsonObject().forEach(entry -> {
// JsonObject resource = entry.getValue().asObject();
// LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").getAsJsonObject());
// ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").asObject());
// ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel);
// lwM2mClient.getResources().put(entry.getName(), resourceValue);
// });
//
// for (JsonObject.Member entry : o.get("sharedAttributes").asObject()) {
// TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder();
// JsonFormat.parser().merge(entry.getValue().getAsString(), builder);
// lwM2mClient.getSharedAttributes().put(entry.getName(), builder.build());
// }
//
// o.get("keyTsLatestMap").asObject().forEach(entry -> {
// lwM2mClient.getKeyTsLatestMap().put(entry.getName(), new AtomicLong(entry.getValue().asLong()));
// });
//
// lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").getAsString()));
//
// Class<LwM2mClient> lwM2mClientClass = LwM2mClient.class;
//
// JsonValue session = o.get("session");
// if (session != null) {
// TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder();
// JsonFormat.parser().merge(session.asString(), builder);
//
// Field sessionField = lwM2mClientClass.getDeclaredField("session");
// sessionField.setAccessible(true);
// sessionField.set(lwM2mClient, builder.build());
// }
//
// JsonValue tenantId = o.get("tenantId");
// if (tenantId != null) {
// Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId");
// tenantIdField.setAccessible(true);
// tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.asString())));
// }
//
// JsonValue deviceId = o.get("deviceId");
// if (tenantId != null) {
// Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId");
// deviceIdField.setAccessible(true);
// deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.asString()));
// }
//
// JsonValue profileId = o.get("profileId");
// if (tenantId != null) {
// Field profileIdField = lwM2mClientClass.getDeclaredField("profileId");
// profileIdField.setAccessible(true);
// profileIdField.set(lwM2mClient, UUID.fromString(profileId.asString()));
// }
//
// JsonValue powerMode = o.get("powerMode");
// if (powerMode != null) {
// Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode");
// powerModeField.setAccessible(true);
// powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.asString()));
// }
//
// JsonValue edrxCycle = o.get("edrxCycle");
// if (edrxCycle != null) {
// Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle");
// edrxCycleField.setAccessible(true);
// edrxCycleField.set(lwM2mClient, edrxCycle.asLong());
// }
//
// JsonValue psmActivityTimer = o.get("psmActivityTimer");
// if (psmActivityTimer != null) {
// Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer");
// psmActivityTimerField.setAccessible(true);
// psmActivityTimerField.set(lwM2mClient, psmActivityTimer.asLong());
// }
//
// JsonValue pagingTransmissionWindow = o.get("pagingTransmissionWindow");
// if (pagingTransmissionWindow != null) {
// Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow");
// pagingTransmissionWindowField.setAccessible(true);
// pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.asLong());
// }
//
// JsonValue registration = o.get("registration");
// if (registration != null) {
// lwM2mClient.setRegistration(RegistrationSerDes.deserialize(registration.asObject()));
// }
//
// lwM2mClient.setAsleep(o.get("asleep").getAsBoolean());
//
// Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime");
// lastUplinkTimeField.setAccessible(true);
// lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").asLong());
//
// Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink");
// firstEdrxDownlinkField.setAccessible(true);
// firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").getAsBoolean());
//
// lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").asInt());
//
// JsonValue lastSentRpcId = o.get("lastSentRpcId");
// if (lastSentRpcId != null) {
// lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.asString()));
// }
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
public void onUpdateValueWithSendRequest(Registration registration, SendRequest sendRequest) {
for(var entry : sendRequest.getNodes().entrySet()) {
for(var entry : sendRequest.getTimestampedNodes().getNodes().entrySet()) {
LwM2mPath path = entry.getKey();
LwM2mNode node = entry.getValue();
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);
} else {
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);
}
});

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;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.gson.JsonElement;
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.ObjectLoader;
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.LwM2mSingleResource;
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.server.registration.Registration;
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.software.SoftwareUpdateResult;
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.Date;
import java.util.HashMap;
@ -62,13 +55,6 @@ import java.util.List;
import java.util.Map;
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.FLOAT;
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_STATE_ID;
@Slf4j
public class LwM2MTransportUtil {
@ -235,7 +222,7 @@ public class LwM2MTransportUtil {
}
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();
try {
String[] keyArray = path.split(LWM2M_SEPARATOR_PATH);
@ -289,28 +276,28 @@ public class LwM2MTransportUtil {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st};
*/
public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, LwM2mUplinkMsgHandler serviceImpl) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
}
private static Attribute[] createWriteAttributes(Object params, LwM2mUplinkMsgHandler serviceImpl, String target) {
List<Attribute> attributeLists = new ArrayList<>();
Map<String, Object> map = JacksonUtil.convertValue(params, new TypeReference<>() {
});
map.forEach((k, v) -> {
if (StringUtils.trimToNull(v.toString()) != null) {
Object attrValue = convertWriteAttributes(k, v, serviceImpl, target);
if (attrValue != null) {
Attribute attribute = createAttribute(k, attrValue);
if (attribute != null) {
attributeLists.add(new Attribute(k, attrValue));
}
}
}
});
return attributeLists.toArray(Attribute[]::new);
}
// public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, LwM2mUplinkMsgHandler serviceImpl) {
// AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
// return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
// }
// private static Attribute[] createWriteAttributes(Object params, LwM2mUplinkMsgHandler serviceImpl, String target) {
// List<Attribute> attributeLists = new ArrayList<>();
// Map<String, Object> map = JacksonUtil.convertValue(params, new TypeReference<>() {
// });
// map.forEach((k, v) -> {
// if (StringUtils.trimToNull(v.toString()) != null) {
// Object attrValue = convertWriteAttributes(k, v, serviceImpl, target);
// if (attrValue != null) {
// Attribute attribute = createAttribute(k, attrValue);
// if (attribute != null) {
// attributeLists.add(new Attribute(k, attrValue));
// }
// }
// }
// });
// return attributeLists.toArray(Attribute[]::new);
// }
/**
* "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) {
Map<Integer, Object> newValues = new HashMap<>();
newValProto.getAsJsonObject().entrySet().forEach((obj) -> {
newValues.put(Integer.valueOf(obj.getKey()), LwM2mValueConverterImpl.getInstance().convertValue(obj.getValue().getAsString(),
STRING, type, new LwM2mPath(fromVersionedIdToObjectId(versionedId))));
newValues.put(Integer.valueOf(obj.getKey()), convertValueByTypeResource (obj.getValue().getAsString(), type, versionedId));
});
return newValues;
}
public static Object convertWriteAttributes(String type, Object value, LwM2mUplinkMsgHandler serviceImpl, String target) {
switch (type) {
/** Integer [0:255]; */
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;
}
public static Object convertValueByTypeResource (String value, ResourceModel.Type type, String versionedId) {
return LwM2mValueConverterImpl.getInstance().convertValue(value,
STRING, type, new LwM2mPath(fromVersionedIdToObjectId(versionedId)));
}
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;
}
}
// public static Object convertWriteAttributes(String type, Object value, LwM2mUplinkMsgHandler serviceImpl, String target) {
// switch (type) {
// /** Integer [0:255]; */
// 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) {
// 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 -

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()) {
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.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) {
throw new DeviceCredentialsValidationException(server + " ShortServerId must not be less than 1 and more than 65534!");
if (!serverConfig.isBootstrapServerIs() && (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534)) {
throw new DeviceCredentialsValidationException("LwM2M 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())) {
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>
<jakarta-annotation.version>2.1.1</jakarta-annotation.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>
<spring-boot.version>3.1.0</spring-boot.version>
<spring-data.version>3.1.0</spring-data.version>
@ -67,13 +67,13 @@
<apache-httpclient.version>4.5.13</apache-httpclient.version>
<apache-httpcore.version>4.4.14</apache-httpcore.version>
<joda-time.version>2.8.1</joda-time.version>
<jackson.version>2.13.4</jackson.version>
<jackson-databind.version>2.13.4.2</jackson-databind.version>
<jackson.version>2.15.3</jackson.version>
<jackson-databind.version>2.15.3</jackson-databind.version>
<fasterxml-classmate.version>1.3.4</fasterxml-classmate.version>
<auth0-jwt.version>4.2.1</auth0-jwt.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version>
<californium.version>3.0.0</californium.version>
<leshan.version>2.0.0-M5</leshan.version>
<californium.version>3.9.1</californium.version>
<leshan.version>2.0.0-M14</leshan.version>
<gson.version>2.9.0</gson.version>
<freemarker.version>2.3.30</freemarker.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 KEY_NAME = 'keyName';
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_PORT_SERVER_NO_SEC = 5685;
export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5687;

Loading…
Cancel
Save