Browse Source

Merge branch 'master' of https://github.com/thingsboard/thingsboard into fix/mqtt-doesnt-close-session-for-removed-device

pull/5945/head
zbeacon 5 years ago
parent
commit
28530cd999
  1. 3
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  2. 3
      application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java
  3. 5
      application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java
  4. 2
      application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
  5. 6
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java
  6. 5
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java
  7. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  8. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java
  9. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  10. 43
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  11. 19
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java
  12. 13
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java
  13. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
  14. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java
  15. 12
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java
  16. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java
  17. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java
  18. 1
      common/cluster-api/src/main/proto/queue.proto
  19. 3
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerService.java
  20. 37
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java
  21. 17
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsCertificateVerifier.java
  22. 11
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionInMemoryStorage.java
  23. 20
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java
  24. 3
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapServerMessageDeliverer.java
  25. 4
      common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeId.java
  26. 4
      common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmDataPageLink.java
  27. 4
      common/data/src/main/java/org/thingsboard/server/common/data/query/AssetSearchQueryFilter.java
  28. 4
      common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceSearchQueryFilter.java
  29. 4
      common/data/src/main/java/org/thingsboard/server/common/data/query/EdgeSearchQueryFilter.java
  30. 4
      common/data/src/main/java/org/thingsboard/server/common/data/query/EntityViewSearchQueryFilter.java
  31. 12
      common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleEngineException.java
  32. 19
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  33. 12
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapMessageObserver.java
  34. 6
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientNoAuth.java
  35. 6
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientX509.java
  36. 23
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  37. 35
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java
  38. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java
  39. 11
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java
  40. 24
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/TbLwM2mVersion.java
  41. 42
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  42. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  43. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MNetworkConfig.java
  44. 19
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  45. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java
  46. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  47. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  48. 13
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java
  49. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java
  50. 7
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java
  51. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java
  52. 5
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java
  53. 2
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
  54. 14
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
  55. 2
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
  56. 70
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
  57. 4
      pom.xml
  58. 67
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java
  59. 13
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
  60. 22
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/NumericParseException.java
  61. 15
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java
  62. 66
      ui-ngx/generate-types.js
  63. 1
      ui-ngx/package.json

3
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -452,6 +452,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
.setRequestId(requestId) .setRequestId(requestId)
.setSharedStateMsg(true) .setSharedStateMsg(true)
.addAllSharedAttributeList(toTsKvProtos(result)) .addAllSharedAttributeList(toTsKvProtos(result))
.setIsMultipleAttributesRequest(request.getSharedAttributeNamesCount() > 1)
.build(); .build();
sendToTransport(responseMsg, sessionInfo); sendToTransport(responseMsg, sessionInfo);
} }
@ -473,6 +474,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
.setRequestId(requestId) .setRequestId(requestId)
.addAllClientAttributeList(toTsKvProtos(result.get(0))) .addAllClientAttributeList(toTsKvProtos(result.get(0)))
.addAllSharedAttributeList(toTsKvProtos(result.get(1))) .addAllSharedAttributeList(toTsKvProtos(result.get(1)))
.setIsMultipleAttributesRequest(
request.getSharedAttributeNamesCount() + request.getClientAttributeNamesCount() > 1)
.build(); .build();
sendToTransport(responseMsg, sessionInfo); sendToTransport(responseMsg, sessionInfo);
} }

3
application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java

@ -96,8 +96,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
} }
}); });
ruleEngineStats.getTenantExceptions().forEach((tenantId, e) -> { ruleEngineStats.getTenantExceptions().forEach((tenantId, e) -> {
TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry("ruleEngineException", TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry("ruleEngineException", e.toJsonString()));
JacksonUtil.toString(JacksonUtil.newObjectNode().put("message", e.getMessage()))));
try { try {
tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), CALLBACK); tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), CALLBACK);
} catch (DataValidationException e2) { } catch (DataValidationException e2) {

5
application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java

@ -28,6 +28,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
@Service @Service
public class EdgeEventsCleanUpService extends AbstractCleanUpService { public class EdgeEventsCleanUpService extends AbstractCleanUpService {
public static final String RANDOM_DELAY_INTERVAL_MS_EXPRESSION =
"#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.edge_events.execution_interval_ms})}";
@Value("${sql.ttl.edge_events.edge_events_ttl}") @Value("${sql.ttl.edge_events.edge_events_ttl}")
private long ttl; private long ttl;
@ -41,7 +44,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService {
this.edgeEventService = edgeEventService; this.edgeEventService = edgeEventService;
} }
@Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") @Scheduled(initialDelayString = RANDOM_DELAY_INTERVAL_MS_EXPRESSION, fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}")
public void cleanUp() { public void cleanUp() {
if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) {
edgeEventService.cleanupEvents(ttl); edgeEventService.cleanupEvents(ttl);

2
application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java

@ -52,6 +52,8 @@ import static org.junit.Assert.assertNotNull;
@Slf4j @Slf4j
public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest { public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest {
protected final byte[] EMPTY_PAYLOAD = new byte[0];
protected CoapClient client; protected CoapClient client;
@Override @Override

6
application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java

@ -25,15 +25,13 @@ import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Slf4j @Slf4j
@ -88,7 +86,7 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends
} }
protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNull(callback.getPayloadBytes()); assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes());
assertNotNull(callback.getObserve()); assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(0, callback.getObserve().intValue()); assertEquals(0, callback.getObserve().intValue());

5
application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java

@ -31,13 +31,12 @@ import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -186,7 +185,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
} }
private void validateCurrentStateNotification(TestCoapCallback callback) { private void validateCurrentStateNotification(TestCoapCallback callback) {
assertNull(callback.getPayloadBytes()); assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes());
assertNotNull(callback.getObserve()); assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode.VALID); assertEquals(callback.getResponseCode(), CoAP.ResponseCode.VALID);
assertEquals(0, callback.getObserve().intValue()); assertEquals(0, callback.getObserve().intValue());

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

@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
@ -174,7 +174,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
public void basicTestConnectionObserveTelemetry(Security security, public void basicTestConnectionObserveTelemetry(Security security,
LwM2MClientCredential credentials, LwM2MClientCredential credentials,
NetworkConfig coapConfig, Configuration coapConfig,
String endpoint) throws Exception { String endpoint) throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION); createDeviceProfile(TRANSPORT_CONFIGURATION);
Device device = createDevice(credentials); Device device = createDevice(credentials);
@ -259,7 +259,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
this.resources = resources; this.resources = resources;
} }
public void createNewClient(Security security, NetworkConfig coapConfig, boolean isRpc, String endpoint) throws Exception { public void createNewClient(Security security, Configuration coapConfig, boolean isRpc, String endpoint) throws Exception {
clientDestroy(); clientDestroy();
client = new LwM2MTestClient(this.executor, endpoint); client = new LwM2MTestClient(this.executor, endpoint);
int clientPort = SocketUtils.findAvailableTcpPort(); int clientPort = SocketUtils.findAvailableTcpPort();

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

@ -15,9 +15,11 @@
*/ */
package org.thingsboard.server.transport.lwm2m; package org.thingsboard.server.transport.lwm2m;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
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.noSec;
public class Lwm2mTestHelper { public class Lwm2mTestHelper {
@ -32,10 +34,10 @@ public class Lwm2mTestHelper {
public static final int SHORT_SERVER_ID = 123; public static final int SHORT_SERVER_ID = 123;
public static final int SHORT_SERVER_ID_BS = 111; public static final int SHORT_SERVER_ID_BS = 111;
public static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); public static final Configuration SECURE_COAP_CONFIG = new Configuration().set(COAP_SECURE_PORT, SECURE_PORT);
public static final String SECURE_URI = "coaps://" + HOST + ":" + SECURE_PORT; public static final String SECURE_URI = "coaps://" + HOST + ":" + SECURE_PORT;
public static final Security SECURITY = noSec("coap://"+ HOST +":" + PORT, SHORT_SERVER_ID); public static final Security SECURITY = noSec("coap://"+ HOST +":" + PORT, SHORT_SERVER_ID);
public static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); public static final Configuration COAP_CONFIG = new Configuration().set(COAP_PORT, PORT);
// Models // Models
public static final String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml", "5.xml", "6.xml", "9.xml", "19.xml", "3303.xml"}; public static final String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml", "5.xml", "6.xml", "9.xml", "19.xml", "3303.xml"};

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

@ -131,10 +131,10 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { try {
state.set(1); state.set(1);
fireResourcesChange(3); fireResourceChange(3);
Thread.sleep(100); Thread.sleep(100);
state.set(2); state.set(2);
fireResourcesChange(3); fireResourceChange(3);
} catch (Exception e) { } catch (Exception e) {
} }
}, 100, TimeUnit.MILLISECONDS); }, 100, TimeUnit.MILLISECONDS);
@ -144,10 +144,10 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { try {
state.set(3); state.set(3);
fireResourcesChange(3); fireResourceChange(3);
Thread.sleep(100); Thread.sleep(100);
updateResult.set(1); updateResult.set(1);
fireResourcesChange(5); fireResourceChange(5);
} catch (Exception e) { } catch (Exception e) {
} }
}, 100, TimeUnit.MILLISECONDS); }, 100, TimeUnit.MILLISECONDS);

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

@ -17,10 +17,7 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.observe.ObservationStore;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.client.californium.LeshanClient; import org.eclipse.leshan.client.californium.LeshanClient;
import org.eclipse.leshan.client.californium.LeshanClientBuilder; import org.eclipse.leshan.client.californium.LeshanClientBuilder;
@ -32,7 +29,6 @@ import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.servers.ServerIdentity; import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.californium.EndpointFactory;
import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectLoader;
@ -44,15 +40,15 @@ import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.DeregisterRequest; import org.eclipse.leshan.core.request.DeregisterRequest;
import org.eclipse.leshan.core.request.RegisterRequest; import org.eclipse.leshan.core.request.RegisterRequest;
import org.eclipse.leshan.core.request.UpdateRequest; import org.eclipse.leshan.core.request.UpdateRequest;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import org.junit.Assert; import org.junit.Assert;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.DEVICE;
import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE;
@ -61,10 +57,10 @@ import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources;
@ -84,7 +80,7 @@ public class LwM2MTestClient {
private LwM2MLocationParams locationParams; private LwM2MLocationParams locationParams;
private LwM2mTemperatureSensor lwM2MTemperatureSensor; private LwM2mTemperatureSensor lwM2MTemperatureSensor;
public void init(Security security, NetworkConfig coapConfig, int port, boolean isRpc) throws InvalidDDFFileException, IOException { public void init(Security security, Configuration coapConfig, int port, boolean isRpc) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", client); Assert.assertNull("client already initialized", client);
List<ObjectModel> models = new ArrayList<>(); List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) { for (String resourceName : resources) {
@ -106,44 +102,19 @@ public class LwM2MTestClient {
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0));
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0), new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12)); initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0), new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12));
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(coapConfig);
dtlsConfig.setRecommendedCipherSuitesOnly(true); dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, true);
dtlsConfig.setClientOnly();
DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory(); DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory();
engineFactory.setReconnectOnUpdate(false); engineFactory.setReconnectOnUpdate(false);
engineFactory.setResumeOnConnect(true); engineFactory.setResumeOnConnect(true);
EndpointFactory endpointFactory = new EndpointFactory() {
@Override
public CoapEndpoint createUnsecuredEndpoint(InetSocketAddress address, NetworkConfig coapConfig,
ObservationStore store) {
CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
builder.setInetSocketAddress(address);
builder.setNetworkConfig(coapConfig);
return builder.build();
}
@Override
public CoapEndpoint createSecuredEndpoint(DtlsConnectorConfig dtlsConfig, NetworkConfig coapConfig,
ObservationStore store) {
CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
DtlsConnectorConfig.Builder dtlsConfigBuilder = new DtlsConnectorConfig.Builder(dtlsConfig);
builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build()));
builder.setNetworkConfig(coapConfig);
return builder.build();
}
};
LeshanClientBuilder builder = new LeshanClientBuilder(endpoint); LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
builder.setLocalAddress("0.0.0.0", port); builder.setLocalAddress("0.0.0.0", port);
builder.setObjects(initializer.createAll()); builder.setObjects(initializer.createAll());
builder.setCoapConfig(coapConfig); builder.setCoapConfig(coapConfig);
builder.setDtlsConfig(dtlsConfig); builder.setDtlsConfig(dtlsConfig);
builder.setRegistrationEngineFactory(engineFactory); builder.setRegistrationEngineFactory(engineFactory);
builder.setEndpointFactory(endpointFactory);
builder.setSharedExecutor(executor); builder.setSharedExecutor(executor);
builder.setDecoder(new DefaultLwM2mDecoder(false)); builder.setDecoder(new DefaultLwM2mDecoder(false));

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

@ -81,8 +81,11 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
public LwM2mBinaryAppDataContainer(ScheduledExecutorService executorService, Integer id) { public LwM2mBinaryAppDataContainer(ScheduledExecutorService executorService, Integer id) {
try { try {
if (id != null) this.setId(id); if (id != null) this.setId(id);
executorService.scheduleWithFixedDelay(() -> executorService.scheduleWithFixedDelay(() -> {
fireResourcesChange(0, 2), 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN fireResourceChange(0);
fireResourceChange(2);
}
, 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN
} catch (Throwable e) { } catch (Throwable e) {
log.error("[{}]Throwable", e.toString()); log.error("[{}]Throwable", e.toString());
e.printStackTrace(); e.printStackTrace();
@ -126,23 +129,23 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
} }
case 1: case 1:
setPriority((Integer) (value.getValue() instanceof Long ? ((Long) value.getValue()).intValue() : value.getValue())); setPriority((Integer) (value.getValue() instanceof Long ? ((Long) value.getValue()).intValue() : value.getValue()));
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
case 2: case 2:
setTimestamp(((Date) value.getValue()).getTime()); setTimestamp(((Date) value.getValue()).getTime());
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
case 3: case 3:
setDescription((String) value.getValue()); setDescription((String) value.getValue());
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
case 4: case 4:
setDataFormat((String) value.getValue()); setDataFormat((String) value.getValue());
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
case 5: case 5:
setAppID((Integer) value.getValue()); setAppID((Integer) value.getValue());
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
default: default:
return super.write(identity, replace, resourceId, value); return super.write(identity, replace, resourceId, value);
@ -185,7 +188,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
try { try {
if (value instanceof LwM2mMultipleResource) { if (value instanceof LwM2mMultipleResource) {
if (replace || this.data == null) { if (replace || this.data == null) {
this.data = new HashMap<>(); this.data = new HashMap<>();
} }
value.getInstances().values().forEach(v -> { value.getInstances().values().forEach(v -> {
this.data.put(v.getId(), (byte[]) v.getValue()); this.data.put(v.getId(), (byte[]) v.getValue());

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

@ -74,8 +74,10 @@ public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable {
} }
this.scaleFactor = scaleFactor; this.scaleFactor = scaleFactor;
timestamp = new Date(); timestamp = new Date();
executorService.scheduleWithFixedDelay(() -> executorService.scheduleWithFixedDelay(() -> {
fireResourcesChange(0, 1), 10000, 10000, TimeUnit.MILLISECONDS); fireResourceChange(0);
fireResourceChange(1);
}, 10000, 10000, TimeUnit.MILLISECONDS);
} catch (Throwable e) { } catch (Throwable e) {
log.error("[{}]Throwable", e.toString()); log.error("[{}]Throwable", e.toString());
e.printStackTrace(); e.printStackTrace();
@ -117,13 +119,16 @@ public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable {
private void moveLatitude(float delta) { private void moveLatitude(float delta) {
latitude = latitude + delta * scaleFactor; latitude = latitude + delta * scaleFactor;
timestamp = new Date(); timestamp = new Date();
fireResourcesChange(0, 5); fireResourceChange(0);
fireResourceChange(5);
} }
private void moveLongitude(float delta) { private void moveLongitude(float delta) {
longitude = longitude + delta * scaleFactor; longitude = longitude + delta * scaleFactor;
timestamp = new Date(); timestamp = new Date();
fireResourcesChange(1, 5); fireResourceChange(1);
fireResourceChange(5);
} }
public float getLatitude() { public float getLatitude() {

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

@ -93,10 +93,9 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
float delta = (RANDOM.nextInt(20) - 10) / 10f; float delta = (RANDOM.nextInt(20) - 10) / 10f;
currentTemp += delta; currentTemp += delta;
Integer changedResource = adjustMinMaxMeasuredValue(currentTemp); Integer changedResource = adjustMinMaxMeasuredValue(currentTemp);
fireResourceChange(5700);
if (changedResource != null) { if (changedResource != null) {
fireResourcesChange(5700, changedResource); fireResourceChange(changedResource);
} else {
fireResourcesChange(5700);
} }
} }
@ -127,5 +126,4 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
} }
} }

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

@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity; import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
@ -25,7 +26,6 @@ import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.response.WriteResponse;
import javax.security.auth.Destroyable;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
@ -103,11 +103,11 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
return WriteResponse.notFound(); return WriteResponse.notFound();
case 14: case 14:
setUtcOffset((String) value.getValue()); setUtcOffset((String) value.getValue());
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
case 15: case 15:
setTimezone((String) value.getValue()); setTimezone((String) value.getValue());
fireResourcesChange(resourceId); fireResourceChange(resourceId);
return WriteResponse.success(); return WriteResponse.success();
default: default:
return super.write(identity, replace, resourceId, value); return super.write(identity, replace, resourceId, value);

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

@ -128,16 +128,17 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
try { try {
state.set(1); state.set(1);
updateResult.set(1); updateResult.set(1);
fireResourcesChange(7, 9); fireResourceChange(7);
fireResourceChange(9);
Thread.sleep(100); Thread.sleep(100);
state.set(2); state.set(2);
fireResourcesChange(7); fireResourceChange(7);
Thread.sleep(100); Thread.sleep(100);
state.set(3); state.set(3);
fireResourcesChange(7); fireResourceChange(7);
Thread.sleep(100); Thread.sleep(100);
updateResult.set(3); updateResult.set(3);
fireResourcesChange(9); fireResourceChange(9);
} catch (Exception e) { } catch (Exception e) {
} }
@ -148,7 +149,8 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
scheduler.schedule(() -> { scheduler.schedule(() -> {
state.set(4); state.set(4);
updateResult.set(2); updateResult.set(2);
fireResourcesChange(7, 9); fireResourceChange(7);
fireResourceChange(9);
}, 100, TimeUnit.MILLISECONDS); }, 100, TimeUnit.MILLISECONDS);
} }

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

@ -201,9 +201,9 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
@Test @Test
public void testSoftwareUpdateByObject9() throws Exception { public void testSoftwareUpdateByObject9() throws Exception {
createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9); NoSecClientCredential credentials = createNoSecClientCredentials(CLIENT_ENDPOINT_OTA9);
final Device device = createDevice(credentials); final Device device = createDevice(credentials);
createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA9); createNewClient(SECURITY, COAP_CONFIG, false, CLIENT_ENDPOINT_OTA9);
Thread.sleep(1000); Thread.sleep(1000);

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

@ -52,8 +52,8 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration
Set actualObjects = ConcurrentHashMap.newKeySet(); Set actualObjects = ConcurrentHashMap.newKeySet();
Set actualInstances = ConcurrentHashMap.newKeySet(); Set actualInstances = ConcurrentHashMap.newKeySet();
rpcActualValue.forEach(node -> { rpcActualValue.forEach(node -> {
if (!node.get("url").asText().equals("/")) { if (!node.get("uriReference").asText().equals("/")) {
LwM2mPath path = new LwM2mPath(node.get("url").asText()); LwM2mPath path = new LwM2mPath(node.get("uriReference").asText());
actualObjects.add("/" + path.getObjectId()); actualObjects.add("/" + path.getObjectId());
if (path.isObjectInstance()) { if (path.isObjectInstance()) {
actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId()); actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId());

1
common/cluster-api/src/main/proto/queue.proto

@ -149,6 +149,7 @@ message GetAttributeResponseMsg {
int32 requestId = 1; int32 requestId = 1;
repeated TsKvProto clientAttributeList = 2; repeated TsKvProto clientAttributeList = 2;
repeated TsKvProto sharedAttributeList = 3; repeated TsKvProto sharedAttributeList = 3;
bool isMultipleAttributesRequest = 4;
string error = 5; string error = 5;
bool sharedStateMsg = 6; bool sharedStateMsg = 6;
} }

3
common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.coapserver;
import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.CoapServer;
import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -24,7 +25,7 @@ public interface CoapServerService {
CoapServer getCoapServer() throws UnknownHostException; CoapServer getCoapServer() throws UnknownHostException;
ConcurrentMap<String, TbCoapDtlsSessionInfo> getDtlsSessionsMap(); ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> getDtlsSessionsMap();
long getTimeout(); long getTimeout();

37
common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java

@ -18,8 +18,9 @@ package org.thingsboard.server.coapserver;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.config.CoapConfig;
import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -37,7 +38,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME; import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS;
@Slf4j @Slf4j
@Component @Component
@ -78,8 +79,8 @@ public class DefaultCoapServerService implements CoapServerService {
} }
@Override @Override
public ConcurrentMap<String, TbCoapDtlsSessionInfo> getDtlsSessionsMap() { public ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> getDtlsSessionsMap() {
return tbDtlsCertificateVerifier != null ? tbDtlsCertificateVerifier.getTbCoapDtlsSessionIdsMap() : null; return tbDtlsCertificateVerifier != null ? tbDtlsCertificateVerifier.getTbCoapDtlsSessionsMap() : null;
} }
@Override @Override
@ -88,16 +89,16 @@ public class DefaultCoapServerService implements CoapServerService {
} }
private CoapServer createCoapServer() throws UnknownHostException { private CoapServer createCoapServer() throws UnknownHostException {
NetworkConfig networkConfig = new NetworkConfig(); Configuration networkConfig = new Configuration();
networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); networkConfig.set(CoapConfig.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); networkConfig.set(CoapConfig.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
networkConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME); networkConfig.set(CoapConfig.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS, TimeUnit.SECONDS);
networkConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); networkConfig.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
networkConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); networkConfig.set(CoapConfig.RESPONSE_MATCHING, CoapConfig.MatcherMode.RELAXED);
networkConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); networkConfig.set(CoapConfig.PREFERRED_BLOCK_SIZE, 1024);
networkConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); networkConfig.set(CoapConfig.MAX_MESSAGE_SIZE, 1024);
networkConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); networkConfig.set(CoapConfig.MAX_RETRANSMIT, 4);
networkConfig.setInt(NetworkConfig.Keys.COAP_PORT, coapServerContext.getPort()); networkConfig.set(CoapConfig.COAP_PORT, coapServerContext.getPort());
server = new CoapServer(networkConfig); server = new CoapServer(networkConfig);
CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder(); CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder();
@ -105,15 +106,15 @@ public class DefaultCoapServerService implements CoapServerService {
InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort());
noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr); noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr);
noSecCoapEndpointBuilder.setNetworkConfig(networkConfig); noSecCoapEndpointBuilder.setConfiguration(networkConfig);
CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build(); CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build();
server.addEndpoint(noSecCoapEndpoint); server.addEndpoint(noSecCoapEndpoint);
if (isDtlsEnabled()) { if (isDtlsEnabled()) {
CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder(); CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder();
TbCoapDtlsSettings dtlsSettings = coapServerContext.getDtlsSettings(); TbCoapDtlsSettings dtlsSettings = coapServerContext.getDtlsSettings();
DtlsConnectorConfig dtlsConnectorConfig = dtlsSettings.dtlsConnectorConfig(); DtlsConnectorConfig dtlsConnectorConfig = dtlsSettings.dtlsConnectorConfig(networkConfig);
networkConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT, dtlsConnectorConfig.getAddress().getPort()); networkConfig.set(CoapConfig.COAP_SECURE_PORT, dtlsConnectorConfig.getAddress().getPort());
dtlsCoapEndpointBuilder.setNetworkConfig(networkConfig); dtlsCoapEndpointBuilder.setConfiguration(networkConfig);
DTLSConnector connector = new DTLSConnector(dtlsConnectorConfig); DTLSConnector connector = new DTLSConnector(dtlsConnectorConfig);
dtlsCoapEndpointBuilder.setConnector(connector); dtlsCoapEndpointBuilder.setConnector(connector);
CoapEndpoint dtlsCoapEndpoint = dtlsCoapEndpointBuilder.build(); CoapEndpoint dtlsCoapEndpoint = dtlsCoapEndpointBuilder.build();

17
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsCertificateVerifier.java

@ -23,7 +23,6 @@ import org.eclipse.californium.scandium.dtls.CertificateMessage;
import org.eclipse.californium.scandium.dtls.CertificateType; import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.CertificateVerificationResult; import org.eclipse.californium.scandium.dtls.CertificateVerificationResult;
import org.eclipse.californium.scandium.dtls.ConnectionId; import org.eclipse.californium.scandium.dtls.ConnectionId;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException; import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
@ -34,13 +33,13 @@ import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import java.net.InetSocketAddress;
import java.security.cert.CertPath; import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateExpiredException;
@ -48,7 +47,6 @@ import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -71,12 +69,12 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
} }
@Override @Override
public List<CertificateType> getSupportedCertificateType() { public List<CertificateType> getSupportedCertificateTypes() {
return Collections.singletonList(CertificateType.X_509); return Collections.singletonList(CertificateType.X_509);
} }
@Override @Override
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) { public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, InetSocketAddress remotePeer, boolean clientUsage, boolean verifySubject, boolean truncateCertificatePath, CertificateMessage message) {
try { try {
CertPath certpath = message.getCertificateChain(); CertPath certpath = message.getCertificateChain();
X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]); X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]);
@ -111,7 +109,7 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
if (msg != null && strCert.equals(msg.getCredentials())) { if (msg != null && strCert.equals(msg.getCredentials())) {
DeviceProfile deviceProfile = msg.getDeviceProfile(); DeviceProfile deviceProfile = msg.getDeviceProfile();
if (msg.hasDeviceInfo() && deviceProfile != null) { if (msg.hasDeviceInfo() && deviceProfile != null) {
tbCoapDtlsSessionInMemoryStorage.put(session.getSessionIdentifier().toString(), new TbCoapDtlsSessionInfo(msg, deviceProfile)); tbCoapDtlsSessionInMemoryStorage.put(remotePeer, new TbCoapDtlsSessionInfo(msg, deviceProfile));
} }
break; break;
} }
@ -120,8 +118,7 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
CertificateExpiredException | CertificateExpiredException |
CertificateNotYetValidException e) { CertificateNotYetValidException e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.BAD_CERTIFICATE, AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.BAD_CERTIFICATE);
session.getPeer());
throw new HandshakeException("Certificate chain could not be validated", alert); throw new HandshakeException("Certificate chain could not be validated", alert);
} }
} }
@ -141,8 +138,8 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
public void setResultHandler(HandshakeResultHandler resultHandler) { public void setResultHandler(HandshakeResultHandler resultHandler) {
} }
public ConcurrentMap<String, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionIdsMap() { public ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionsMap() {
return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionIdMap(); return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionsMap();
} }
public void evictTimeoutSessions() { public void evictTimeoutSessions() {

11
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionInMemoryStorage.java

@ -18,6 +18,7 @@ package org.thingsboard.server.coapserver;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -25,7 +26,7 @@ import java.util.concurrent.ConcurrentMap;
@Data @Data
public class TbCoapDtlsSessionInMemoryStorage { public class TbCoapDtlsSessionInMemoryStorage {
private final ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap = new ConcurrentHashMap<>(); private final ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> dtlsSessionsMap = new ConcurrentHashMap<>();
private long dtlsSessionInactivityTimeout; private long dtlsSessionInactivityTimeout;
private long dtlsSessionReportTimeout; private long dtlsSessionReportTimeout;
@ -35,14 +36,14 @@ public class TbCoapDtlsSessionInMemoryStorage {
this.dtlsSessionReportTimeout = dtlsSessionReportTimeout; this.dtlsSessionReportTimeout = dtlsSessionReportTimeout;
} }
public void put(String dtlsSessionId, TbCoapDtlsSessionInfo dtlsSessionInfo) { public void put(InetSocketAddress remotePeer, TbCoapDtlsSessionInfo dtlsSessionInfo) {
log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", dtlsSessionId, dtlsSessionInfo.getLastActivityTime()); log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", remotePeer, dtlsSessionInfo.getLastActivityTime());
dtlsSessionIdMap.putIfAbsent(dtlsSessionId, dtlsSessionInfo); dtlsSessionsMap.putIfAbsent(remotePeer, dtlsSessionInfo);
} }
public void evictTimeoutSessions() { public void evictTimeoutSessions() {
long expTime = System.currentTimeMillis() - dtlsSessionInactivityTimeout; long expTime = System.currentTimeMillis() - dtlsSessionInactivityTimeout;
dtlsSessionIdMap.entrySet().removeIf(entry -> { dtlsSessionsMap.entrySet().removeIf(entry -> {
if (entry.getValue().getLastActivityTime() < expTime) { if (entry.getValue().getLastActivityTime() < expTime) {
log.trace("DTLS session was removed from in-memory store: [{}]", entry.getKey()); log.trace("DTLS session was removed from in-memory store: [{}]", entry.getKey());
return true; return true;

20
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java

@ -16,9 +16,13 @@
package org.thingsboard.server.coapserver; package org.thingsboard.server.coapserver;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.config.CertificateAuthenticationMode;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.CertificateType; import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -26,17 +30,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials; import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig; import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.Collections; import java.util.Collections;
@Slf4j @Slf4j
@ -75,15 +76,14 @@ public class TbCoapDtlsSettings {
@Autowired @Autowired
private TbServiceInfoProvider serviceInfoProvider; private TbServiceInfoProvider serviceInfoProvider;
public DtlsConnectorConfig dtlsConnectorConfig() throws UnknownHostException { public DtlsConnectorConfig dtlsConnectorConfig(Configuration configuration) throws UnknownHostException {
DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(configuration);
configBuilder.setAddress(getInetSocketAddress()); configBuilder.setAddress(getInetSocketAddress());
SslCredentials sslCredentials = this.coapDtlsCredentialsConfig.getCredentials(); SslCredentials sslCredentials = this.coapDtlsCredentialsConfig.getCredentials();
SslContextUtil.Credentials serverCredentials = SslContextUtil.Credentials serverCredentials =
new SslContextUtil.Credentials(sslCredentials.getPrivateKey(), null, sslCredentials.getCertificateChain()); new SslContextUtil.Credentials(sslCredentials.getPrivateKey(), null, sslCredentials.getCertificateChain());
configBuilder.setServerOnly(true); configBuilder.set(DtlsConfig.DTLS_ROLE, DtlsConfig.DtlsRole.SERVER_ONLY);
configBuilder.setClientAuthenticationRequired(false); configBuilder.set(DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE, CertificateAuthenticationMode.WANTED);
configBuilder.setClientAuthenticationWanted(true);
configBuilder.setAdvancedCertificateVerifier( configBuilder.setAdvancedCertificateVerifier(
new TbCoapDtlsCertificateVerifier( new TbCoapDtlsCertificateVerifier(
transportService, transportService,
@ -93,8 +93,8 @@ public class TbCoapDtlsSettings {
skipValidityCheckForClientCert skipValidityCheckForClientCert
) )
); );
configBuilder.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), configBuilder.setCertificateIdentityProvider(new SingleCertificateProvider(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(),
Collections.singletonList(CertificateType.X_509)); Collections.singletonList(CertificateType.X_509)));
return configBuilder.build(); return configBuilder.build();
} }

3
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapServerMessageDeliverer.java

@ -18,6 +18,7 @@ package org.thingsboard.server.coapserver;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.OptionSet; import org.eclipse.californium.core.coap.OptionSet;
import org.eclipse.californium.core.network.Exchange; import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.server.DelivererException;
import org.eclipse.californium.core.server.ServerMessageDeliverer; import org.eclipse.californium.core.server.ServerMessageDeliverer;
import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.Resource;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -32,7 +33,7 @@ public class TbCoapServerMessageDeliverer extends ServerMessageDeliverer {
} }
@Override @Override
protected Resource findResource(Exchange exchange) { protected Resource findResource(Exchange exchange) throws DelivererException {
validateUriPath(exchange); validateUriPath(exchange);
return findResource(exchange.getRequest().getOptions().getUriPath()); return findResource(exchange.getRequest().getOptions().getUriPath());
} }

4
common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeId.java

@ -32,8 +32,8 @@ public class EdgeId extends UUIDBased implements EntityId {
super(id); super(id);
} }
public static EdgeId fromString(String integrationId) { public static EdgeId fromString(String edgeId) {
return new EdgeId(UUID.fromString(integrationId)); return new EdgeId(UUID.fromString(edgeId));
} }
@ApiModelProperty(position = 2, required = true, value = "string", example = "EDGE", allowableValues = "EDGE") @ApiModelProperty(position = 2, required = true, value = "string", example = "EDGE", allowableValues = "EDGE")

4
common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmDataPageLink.java

@ -18,7 +18,9 @@ package org.thingsboard.server.common.data.query;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
@ -26,6 +28,8 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus;
import java.util.List; import java.util.List;
@Data @Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@AllArgsConstructor @AllArgsConstructor
public class AlarmDataPageLink extends EntityDataPageLink { public class AlarmDataPageLink extends EntityDataPageLink {

4
common/data/src/main/java/org/thingsboard/server/common/data/query/AssetSearchQueryFilter.java

@ -16,10 +16,14 @@
package org.thingsboard.server.common.data.query; package org.thingsboard.server.common.data.query;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List; import java.util.List;
@Data @Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AssetSearchQueryFilter extends EntitySearchQueryFilter { public class AssetSearchQueryFilter extends EntitySearchQueryFilter {
@Override @Override

4
common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceSearchQueryFilter.java

@ -16,10 +16,14 @@
package org.thingsboard.server.common.data.query; package org.thingsboard.server.common.data.query;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List; import java.util.List;
@Data @Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DeviceSearchQueryFilter extends EntitySearchQueryFilter { public class DeviceSearchQueryFilter extends EntitySearchQueryFilter {
@Override @Override

4
common/data/src/main/java/org/thingsboard/server/common/data/query/EdgeSearchQueryFilter.java

@ -16,10 +16,14 @@
package org.thingsboard.server.common.data.query; package org.thingsboard.server.common.data.query;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List; import java.util.List;
@Data @Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EdgeSearchQueryFilter extends EntitySearchQueryFilter { public class EdgeSearchQueryFilter extends EntitySearchQueryFilter {
@Override @Override

4
common/data/src/main/java/org/thingsboard/server/common/data/query/EntityViewSearchQueryFilter.java

@ -16,10 +16,14 @@
package org.thingsboard.server.common.data.query; package org.thingsboard.server.common.data.query;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List; import java.util.List;
@Data @Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EntityViewSearchQueryFilter extends EntitySearchQueryFilter { public class EntityViewSearchQueryFilter extends EntitySearchQueryFilter {
@Override @Override

12
common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleEngineException.java

@ -25,11 +25,19 @@ public class RuleEngineException extends Exception {
protected static final ObjectMapper mapper = new ObjectMapper(); protected static final ObjectMapper mapper = new ObjectMapper();
@Getter @Getter
private final long ts; private long ts;
public RuleEngineException(String message) { public RuleEngineException(String message) {
super(message != null ? message : "Unknown"); super(message != null ? message : "Unknown");
this.ts = System.currentTimeMillis(); ts = System.currentTimeMillis();
} }
public String toJsonString() {
try {
return mapper.writeValueAsString(mapper.createObjectNode().put("message", getMessage()));
} catch (JsonProcessingException e) {
log.warn("Failed to serialize exception ", e);
throw new RuntimeException(e);
}
}
} }

19
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -30,7 +30,6 @@ import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.FeatureType;
@ -48,6 +47,7 @@ import org.thingsboard.server.transport.coap.callback.ToServerRpcSyncSessionCall
import org.thingsboard.server.transport.coap.client.CoapClientContext; import org.thingsboard.server.transport.coap.client.CoapClientContext;
import org.thingsboard.server.transport.coap.client.TbCoapClientState; import org.thingsboard.server.transport.coap.client.TbCoapClientState;
import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
@ -56,6 +56,8 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static org.eclipse.californium.elements.DtlsEndpointContext.KEY_SESSION_ID;
@Slf4j @Slf4j
public class CoapTransportResource extends AbstractCoapTransportResource { public class CoapTransportResource extends AbstractCoapTransportResource {
private static final int ACCESS_TOKEN_POSITION = 3; private static final int ACCESS_TOKEN_POSITION = 3;
@ -64,9 +66,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
private static final int FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST = 3; private static final int FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST = 3;
private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4; private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4;
private static final String DTLS_SESSION_ID_KEY = "DTLS_SESSION_ID";
private final ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap; private final ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> dtlsSessionsMap;
private final long timeout; private final long timeout;
private final CoapClientContext clients; private final CoapClientContext clients;
@ -74,7 +75,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
super(ctx, name); super(ctx, name);
this.setObservable(true); // enable observing this.setObservable(true); // enable observing
this.addObserver(new CoapResourceObserver()); this.addObserver(new CoapResourceObserver());
this.dtlsSessionIdMap = coapServerService.getDtlsSessionsMap(); this.dtlsSessionsMap = coapServerService.getDtlsSessionsMap();
this.timeout = coapServerService.getTimeout(); this.timeout = coapServerService.getTimeout();
this.clients = ctx.getClientContext(); this.clients = ctx.getClientContext();
long sessionReportTimeout = ctx.getSessionReportTimeout(); long sessionReportTimeout = ctx.getSessionReportTimeout();
@ -91,7 +92,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
if (relation == null || relation.isCanceled()) { if (relation == null || relation.isCanceled()) {
return; // because request did not try to establish a relation return; // because request did not try to establish a relation
} }
if (CoAP.ResponseCode.isSuccess(response.getCode())) { if (response.getCode().isSuccess()) {
if (!relation.isEstablished()) { if (!relation.isEstablished()) {
relation.setEstablished(); relation.setEstablished();
addObserveRelation(relation); addObserveRelation(relation);
@ -198,10 +199,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
Exchange advanced = exchange.advanced(); Exchange advanced = exchange.advanced();
Request request = advanced.getRequest(); Request request = advanced.getRequest();
String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY); var dtlsSessionId = request.getSourceContext().get(KEY_SESSION_ID);
if (dtlsSessionIdMap != null && StringUtils.isNotEmpty(dtlsSessionIdStr)) { if (dtlsSessionsMap != null && dtlsSessionId != null && !dtlsSessionId.isEmpty()) {
TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionsMap
.computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> { .computeIfPresent(request.getSourceContext().getPeerAddress(), (dtlsSessionIdStr, dtlsSessionInfo) -> {
dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis()); dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis());
return dtlsSessionInfo; return dtlsSessionInfo;
}); });

12
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapMessageObserver.java

@ -29,6 +29,11 @@ public class TbCoapMessageObserver implements MessageObserver {
private final Consumer<Integer> onAcknowledge; private final Consumer<Integer> onAcknowledge;
private final Consumer<Integer> onTimeout; private final Consumer<Integer> onTimeout;
@Override
public boolean isInternal() {
return false;
}
@Override @Override
public void onRetransmission() { public void onRetransmission() {
@ -86,13 +91,18 @@ public class TbCoapMessageObserver implements MessageObserver {
} }
@Override
public void onResponseHandlingError(Throwable cause) {
}
@Override @Override
public void onContextEstablished(EndpointContext endpointContext) { public void onContextEstablished(EndpointContext endpointContext) {
} }
@Override @Override
public void onComplete() { public void onTransferComplete() {
} }
} }

6
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientNoAuth.java

@ -21,11 +21,13 @@ import org.eclipse.californium.core.Utils;
import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.elements.DtlsEndpointContext; import org.eclipse.californium.elements.DtlsEndpointContext;
import org.eclipse.californium.elements.EndpointContext; import org.eclipse.californium.elements.EndpointContext;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.elements.exception.ConnectorException; import org.eclipse.californium.elements.exception.ConnectorException;
import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.CertificateType; import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider;
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
@ -118,7 +120,7 @@ public class SecureClientNoAuth {
String keyStorePassword = args[6]; String keyStorePassword = args[6];
DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(new Configuration());
setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword); setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword);
DTLSConnector dtlsConnector = new DTLSConnector(builder.build()); DTLSConnector dtlsConnector = new DTLSConnector(builder.build());
SecureClientNoAuth client = new SecureClientNoAuth(dtlsConnector, host, port, accessToken, clientKeys, sharedKeys); SecureClientNoAuth client = new SecureClientNoAuth(dtlsConnector, host, port, accessToken, clientKeys, sharedKeys);
@ -134,7 +136,7 @@ public class SecureClientNoAuth {
keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray()); keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray());
trustBuilder.setTrustedCertificates(trustedCertificates); trustBuilder.setTrustedCertificates(trustedCertificates);
config.setAdvancedCertificateVerifier(trustBuilder.build()); config.setAdvancedCertificateVerifier(trustBuilder.build());
config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)); config.setCertificateIdentityProvider(new SingleCertificateProvider(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)));
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
System.err.println("certificates are invalid!"); System.err.println("certificates are invalid!");
throw new IllegalArgumentException(e.getMessage()); throw new IllegalArgumentException(e.getMessage());

6
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientX509.java

@ -21,11 +21,13 @@ import org.eclipse.californium.core.Utils;
import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.elements.DtlsEndpointContext; import org.eclipse.californium.elements.DtlsEndpointContext;
import org.eclipse.californium.elements.EndpointContext; import org.eclipse.californium.elements.EndpointContext;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.elements.exception.ConnectorException; import org.eclipse.californium.elements.exception.ConnectorException;
import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.CertificateType; import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider;
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
@ -117,7 +119,7 @@ public class SecureClientX509 {
String keyStorePassword = args[5]; String keyStorePassword = args[5];
DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(new Configuration());
setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword); setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword);
DTLSConnector dtlsConnector = new DTLSConnector(builder.build()); DTLSConnector dtlsConnector = new DTLSConnector(builder.build());
SecureClientX509 client = new SecureClientX509(dtlsConnector, host, port, clientKeys, sharedKeys); SecureClientX509 client = new SecureClientX509(dtlsConnector, host, port, clientKeys, sharedKeys);
@ -133,7 +135,7 @@ public class SecureClientX509 {
keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray()); keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray());
trustBuilder.setTrustedCertificates(trustedCertificates); trustBuilder.setTrustedCertificates(trustedCertificates);
config.setAdvancedCertificateVerifier(trustBuilder.build()); config.setAdvancedCertificateVerifier(trustBuilder.build());
config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)); config.setCertificateIdentityProvider(new SingleCertificateProvider(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)));
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
System.err.println("certificates are invalid!"); System.err.println("certificates are invalid!");
throw new IllegalArgumentException(e.getMessage()); throw new IllegalArgumentException(e.getMessage());

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

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
@ -31,12 +32,14 @@ import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecu
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore; import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
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.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.PSK_CIPHER_SUITES;
import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES; import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig; import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig;
@ -45,7 +48,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.g
@TbLwM2mBootstrapTransportComponent @TbLwM2mBootstrapTransportComponent
@RequiredArgsConstructor @RequiredArgsConstructor
public class LwM2MTransportBootstrapService { public class LwM2MTransportBootstrapService {
private boolean pskMode = false;
private final LwM2MTransportServerConfig serverConfig; private final LwM2MTransportServerConfig serverConfig;
private final LwM2MTransportBootstrapConfig bootstrapConfig; private final LwM2MTransportBootstrapConfig bootstrapConfig;
@ -80,12 +82,13 @@ public class LwM2MTransportBootstrapService {
/* Create and Set DTLS Config */ /* Create and Set DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers()); dtlsConfig.set(DtlsConfig.DTLS_ROLE, DtlsConfig.DtlsRole.SERVER_ONLY);
dtlsConfig.setSupportedCipherSuites(this.pskMode ? DefaultLwM2mTransportService.PSK_CIPHER_SUITES : DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES); dtlsConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, serverConfig.isRecommendedSupportedGroups());
/* Create credentials */ dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, serverConfig.isRecommendedCiphers());
this.setServerWithCredentials(builder, dtlsConfig);
setServerWithCredentials(builder, dtlsConfig);
/* Set DTLS Config */ /* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig); builder.setDtlsConfig(dtlsConfig);
@ -110,13 +113,13 @@ public class LwM2MTransportBootstrapService {
builder.setPublicKey(sslCredentials.getPublicKey()); builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey()); builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain()); builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES);
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier); dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else { } else {
/* by default trust all */ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]); builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for BootStrapServer"); log.info("Unable to load X509 files for BootStrapServer");
this.pskMode = true; dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, PSK_CIPHER_SUITES);
} }
} }
} }

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

@ -17,19 +17,18 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.auth.RawPublicKeyIdentity;
import org.eclipse.californium.elements.util.CertPathUtil; import org.eclipse.californium.elements.util.CertPathUtil;
import org.eclipse.californium.scandium.dtls.AlertMessage; import org.eclipse.californium.scandium.dtls.AlertMessage;
import org.eclipse.californium.scandium.dtls.CertificateMessage; import org.eclipse.californium.scandium.dtls.CertificateMessage;
import org.eclipse.californium.scandium.dtls.CertificateType; import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.CertificateVerificationResult; import org.eclipse.californium.scandium.dtls.CertificateVerificationResult;
import org.eclipse.californium.scandium.dtls.ConnectionId; import org.eclipse.californium.scandium.dtls.ConnectionId;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException; import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames; import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
@ -39,14 +38,12 @@ import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.queue.util.TbLwM2mBootstrapTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mBootstrapTransportComponent;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import java.net.InetSocketAddress;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.CertPath; import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
@ -65,14 +62,13 @@ public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCerti
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
private final LwM2MBootstrapSecurityStore bsSecurityStore; private final LwM2MBootstrapSecurityStore bsSecurityStore;
@SuppressWarnings("deprecation") private StaticNewAdvancedCertificateVerifier staticCertificateVerifier;
private StaticCertificateVerifier staticCertificateVerifier;
@Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}") @Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}")
private boolean skipValidityCheckForClientCert; private boolean skipValidityCheckForClientCert;
@Override @Override
public List<CertificateType> getSupportedCertificateType() { public List<CertificateType> getSupportedCertificateTypes() {
return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY); return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY);
} }
@ -83,17 +79,17 @@ public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCerti
/* by default trust all */ /* by default trust all */
if (config.getTrustSslCredentials() != null) { if (config.getTrustSslCredentials() != null) {
X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates(); X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates();
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates); staticCertificateVerifier = new StaticNewAdvancedCertificateVerifier(trustedCertificates, new RawPublicKeyIdentity[0], null);
} }
} catch (Exception e) { } catch (Exception e) {
log.info("Failed to initialize the certificate verifier", e); log.warn("ailed to initialize the LwM2M certificate verifier", e);
} }
} }
@Override @Override
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, InetSocketAddress remotePeer,
boolean truncateCertificatePath, CertificateMessage message, boolean clientUsage, boolean verifySubject, boolean truncateCertificatePath,
DTLSSession session) { CertificateMessage message) {
CertPath certChain = message.getCertificateChain(); CertPath certChain = message.getCertificateChain();
if (certChain == null) { if (certChain == null) {
//We trust all RPK on this layer, and use TbLwM2MAuthorizer //We trust all RPK on this layer, and use TbLwM2MAuthorizer
@ -111,14 +107,14 @@ public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCerti
TbLwM2MSecurityInfo securityInfo = null; TbLwM2MSecurityInfo securityInfo = null;
// verify if trust // verify if trust
if (staticCertificateVerifier != null) { if (staticCertificateVerifier != null) {
try { HandshakeException exception = staticCertificateVerifier.verifyCertificate(cid, serverName, remotePeer, clientUsage, verifySubject, truncateCertificatePath, message).getException();
staticCertificateVerifier.verifyCertificate(message, session); if (exception == null) {
String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN"); String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN");
if (StringUtils.isNotEmpty(endpoint)) { if (StringUtils.isNotEmpty(endpoint)) {
securityInfo = bsSecurityStore.getX509ByEndpoint(endpoint); securityInfo = bsSecurityStore.getX509ByEndpoint(endpoint);
} }
} catch (HandshakeException e) { } else {
log.trace("Certificate validation failed.", e); log.trace("Certificate validation failed.", exception);
} }
} }
// if not trust or cert trust securityInfo == null // if not trust or cert trust securityInfo == null
@ -143,8 +139,7 @@ public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCerti
} }
} }
if (!x509CredentialsFound) { if (!x509CredentialsFound) {
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR, AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR);
session.getPeer());
throw new HandshakeException("x509 verification not enabled!", alert); throw new HandshakeException("x509 verification not enabled!", alert);
} }
return new CertificateVerificationResult(cid, certChain, null); return new CertificateVerificationResult(cid, certChain, null);

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

@ -16,7 +16,7 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.store; package org.thingsboard.server.transport.lwm2m.bootstrap.store;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObject;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.BootstrapDeleteRequest; import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
@ -134,18 +134,18 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi
log.info("Object after discover: [{}]", objectLinks); log.info("Object after discover: [{}]", objectLinks);
this.securityInstances = new HashMap<>(); this.securityInstances = new HashMap<>();
for (Link link : objectLinks) { for (Link link : objectLinks) {
if (link.getUrl().startsWith("/0/")) { if (link.getUriReference().startsWith("/0/")) {
try { try {
LwM2mPath path = new LwM2mPath(link.getUrl()); LwM2mPath path = new LwM2mPath(link.getUriReference());
if (path.isObjectInstance()) { if (path.isObjectInstance()) {
if (link.getAttributes().containsKey("ssid")) { if (link.getLinkParams().containsKey("ssid")) {
int serverId = Integer.parseInt(link.getAttributes().get("ssid")); int serverId = Integer.parseInt(link.getLinkParams().get("ssid").getUnquoted());
if (!this.securityInstances.containsKey(serverId)) { if (!this.securityInstances.containsKey(serverId)) {
this.securityInstances.put(serverId, path.getObjectInstanceId()); this.securityInstances.put(serverId, path.getObjectInstanceId());
} else { } else {
log.error("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId()); log.error("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId());
} }
this.securityInstances.put(Integer.valueOf(link.getAttributes().get("ssid")), path.getObjectInstanceId()); this.securityInstances.put(Integer.valueOf(link.getLinkParams().get("ssid").getUnquoted()), path.getObjectInstanceId());
} else { } else {
if (!this.securityInstances.containsKey(0)) { if (!this.securityInstances.containsKey(0)) {
this.securityInstances.put(0, path.getObjectInstanceId()); this.securityInstances.put(0, path.getObjectInstanceId());
@ -188,10 +188,10 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvi
private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) { private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) {
Link[] links = response.getObjectLinks(); Link[] links = response.getObjectLinks();
Arrays.stream(links).forEach(link -> { Arrays.stream(links).forEach(link -> {
LwM2mPath path = new LwM2mPath(link.getUrl()); LwM2mPath path = new LwM2mPath(link.getUriReference());
if (!path.isRoot() && path.getObjectId() < 3) { if (!path.isRoot() && path.getObjectId() < 3) {
if (path.isObject()) { if (path.isObject()) {
String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver") : "1.0"; String ver = link.getLinkParams().get("ver") != null ? link.getLinkParams().get("ver").getUnquoted() : "1.0";
this.supportedObjects.put(path.getObjectId(), ver); this.supportedObjects.put(path.getObjectId(), ver);
} }
} }

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

@ -80,7 +80,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */ /* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigSecurityInfo(store);
BootstrapConfig bsConfig = store.getBootstrapConfig(); BootstrapConfig bsConfig = store.getBootstrapConfig();
if (bsConfig.security != null) { if (bsConfig.security != null) {
try { try {
@ -99,18 +99,13 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
public TbLwM2MSecurityInfo getX509ByEndpoint(String endPoint) { public TbLwM2MSecurityInfo getX509ByEndpoint(String endPoint) {
try {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
this.addValueToStore(store, store.getEndpoint()); this.addValueToStore(store, store.getEndpoint());
return store; return store;
} catch (LwM2MAuthException e) {
log.trace("Failed find security info: {}", endPoint, e);
}
return null;
} }
private void setBootstrapConfigScurityInfo(TbLwM2MSecurityInfo store) { private void setBootstrapConfigSecurityInfo(TbLwM2MSecurityInfo store) {
/* BootstrapConfig */ /* BootstrapConfig */
LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store); LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
if (lwM2MBootstrapConfig != null) { if (lwM2MBootstrapConfig != null) {
@ -182,7 +177,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
SecurityInfo securityInfo = null; SecurityInfo securityInfo = null;
if (store != null && store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { if (store != null && store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
securityInfo = store.getSecurityInfo(); securityInfo = store.getSecurityInfo();
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigSecurityInfo(store);
BootstrapConfig bsConfigNew = store.getBootstrapConfig(); BootstrapConfig bsConfigNew = store.getBootstrapConfig();
if (bsConfigNew != null) { if (bsConfigNew != null) {
try { try {

24
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2mVersion.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/TbLwM2mVersion.java

@ -16,31 +16,31 @@
package org.thingsboard.server.transport.lwm2m.config; package org.thingsboard.server.transport.lwm2m.config;
import lombok.Getter; import lombok.Getter;
import org.eclipse.leshan.core.LwM2m.Version; import org.eclipse.leshan.core.LwM2m.LwM2mVersion;
import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.ContentFormat;
public enum LwM2mVersion { public enum TbLwM2mVersion {
VERSION_1_0(0, Version.V1_0, ContentFormat.TLV, false), VERSION_1_0(0, LwM2mVersion.V1_0, ContentFormat.TLV, false),
VERSION_1_1(1, Version.V1_1, ContentFormat.TEXT, true); VERSION_1_1(1, LwM2mVersion.V1_1, ContentFormat.TEXT, true);
@Getter @Getter
private final int code; private final int code;
@Getter @Getter
private final Version version; private final LwM2mVersion version;
@Getter @Getter
private final ContentFormat contentFormat; private final ContentFormat contentFormat;
@Getter @Getter
private final boolean composite; private final boolean composite;
LwM2mVersion(int code, Version version, ContentFormat contentFormat, boolean composite) { TbLwM2mVersion(int code, LwM2mVersion version, ContentFormat contentFormat, boolean composite) {
this.code = code; this.code = code;
this.version = version; this.version = version;
this.contentFormat = contentFormat; this.contentFormat = contentFormat;
this.composite = composite; this.composite = composite;
} }
public static LwM2mVersion fromVersion(Version version) { public static TbLwM2mVersion fromVersion(LwM2mVersion version) {
for (LwM2mVersion to : LwM2mVersion.values()) { for (TbLwM2mVersion to : TbLwM2mVersion.values()) {
if (to.version.equals(version)) { if (to.version.equals(version)) {
return to; return to;
} }
@ -48,8 +48,8 @@ public enum LwM2mVersion {
throw new IllegalArgumentException(String.format("Unsupported typeLwM2mVersion type : %s", version)); throw new IllegalArgumentException(String.format("Unsupported typeLwM2mVersion type : %s", version));
} }
public static LwM2mVersion fromVersionStr(String versionStr) { public static TbLwM2mVersion fromVersionStr(String versionStr) {
for (LwM2mVersion to : LwM2mVersion.values()) { for (TbLwM2mVersion to : TbLwM2mVersion.values()) {
if (to.version.toString().equals(versionStr)) { if (to.version.toString().equals(versionStr)) {
return to; return to;
} }
@ -57,8 +57,8 @@ public enum LwM2mVersion {
throw new IllegalArgumentException(String.format("Unsupported contentFormatLwM2mVersion version : %s", versionStr)); throw new IllegalArgumentException(String.format("Unsupported contentFormatLwM2mVersion version : %s", versionStr));
} }
public static LwM2mVersion fromCode(int code) { public static TbLwM2mVersion fromCode(int code) {
for (LwM2mVersion to : LwM2mVersion.values()) { for (TbLwM2mVersion to : TbLwM2mVersion.values()) {
if (to.code == code) { if (to.code == code) {
return to; return to;
} }

42
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java

@ -17,17 +17,17 @@ package org.thingsboard.server.transport.lwm2m.secure;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.auth.RawPublicKeyIdentity;
import org.eclipse.californium.elements.util.CertPathUtil; import org.eclipse.californium.elements.util.CertPathUtil;
import org.eclipse.californium.scandium.dtls.AlertMessage; import org.eclipse.californium.scandium.dtls.AlertMessage;
import org.eclipse.californium.scandium.dtls.CertificateMessage; import org.eclipse.californium.scandium.dtls.CertificateMessage;
import org.eclipse.californium.scandium.dtls.CertificateType; import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.CertificateVerificationResult; import org.eclipse.californium.scandium.dtls.CertificateVerificationResult;
import org.eclipse.californium.scandium.dtls.ConnectionId; import org.eclipse.californium.scandium.dtls.ConnectionId;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException; import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames; import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -49,6 +49,7 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import java.net.InetSocketAddress;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.CertPath; import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
@ -71,34 +72,33 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator; private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator;
private final TbMainSecurityStore securityStore; private final TbMainSecurityStore securityStore;
@SuppressWarnings("deprecation") private StaticNewAdvancedCertificateVerifier staticCertificateVerifier;
private StaticCertificateVerifier staticCertificateVerifier;
@Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}") @Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}")
private boolean skipValidityCheckForClientCert; private boolean skipValidityCheckForClientCert;
@Override @Override
public List<CertificateType> getSupportedCertificateType() { public List<CertificateType> getSupportedCertificateTypes() {
return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY); return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY);
} }
@SuppressWarnings("deprecation")
@PostConstruct @PostConstruct
public void init() { public void init() {
try { try {
/* by default trust all */ /* by default trust all */
if (config.getTrustSslCredentials() != null) { if (config.getTrustSslCredentials() != null) {
X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates(); X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates();
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates); staticCertificateVerifier = new StaticNewAdvancedCertificateVerifier(trustedCertificates, new RawPublicKeyIdentity[0], null);
} }
} catch (Exception e) { } catch (Exception e) {
log.info("Failed to initialize the "); log.warn("Failed to initialize the LwM2M certificate verifier", e);
} }
} }
@Override @Override
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) { public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, InetSocketAddress remotePeer,
boolean clientUsage, boolean verifySubject, boolean truncateCertificatePath,
CertificateMessage message) {
CertPath certChain = message.getCertificateChain(); CertPath certChain = message.getCertificateChain();
if (certChain == null) { if (certChain == null) {
//We trust all RPK on this layer, and use TbLwM2MAuthorizer //We trust all RPK on this layer, and use TbLwM2MAuthorizer
@ -115,21 +115,24 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
} }
TbLwM2MSecurityInfo securityInfo = null; TbLwM2MSecurityInfo securityInfo = null;
if (staticCertificateVerifier != null) { if (staticCertificateVerifier != null) {
try { HandshakeException exception = staticCertificateVerifier.verifyCertificate(cid, serverName, remotePeer, clientUsage, verifySubject, truncateCertificatePath, message).getException();
staticCertificateVerifier.verifyCertificate(message, session); if (exception == null) {
String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN"); try {
if (StringUtils.isNotEmpty(endpoint)) { String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN");
securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT); if (StringUtils.isNotEmpty(endpoint)) {
securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT);
}
} catch (LwM2MAuthException e) {
log.trace("Certificate trust validation failed.", e);
} }
} catch (HandshakeException | LwM2MAuthException e) { } else {
log.trace("Certificate trust validation failed.", e); log.trace("Certificate trust validation failed.", exception);
} }
} }
// if not trust or cert trust securityInfo == null // if not trust or cert trust securityInfo == null
String strCert = SslUtil.getCertificateString(cert); String strCert = SslUtil.getCertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
if (securityInfo == null || securityInfo.getMsg() == null) { if (securityInfo == null || securityInfo.getMsg() == null) {
try { try {
securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT); securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT);
} catch (LwM2MAuthException e) { } catch (LwM2MAuthException e) {
@ -168,8 +171,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
} }
} }
if (!x509CredentialsFound) { if (!x509CredentialsFound) {
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR, AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR);
session.getPeer());
throw new HandshakeException("x509 verification not enabled!", alert); throw new HandshakeException("x509 verification not enabled!", alert);
} }
return new CertificateVerificationResult(cid, certChain, null); return new CertificateVerificationResult(cid, certChain, null);

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

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder;
@ -40,6 +41,8 @@ import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
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.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
@ -121,11 +124,12 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
/* Create DTLS Config */ /* Create DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(getCoapConfig(config.getPort(), config.getSecurePort(), config));
dtlsConfig.set(DtlsConfig.DTLS_ROLE, DtlsConfig.DtlsRole.SERVER_ONLY);
dtlsConfig.set(DTLS_RECOMMENDED_CURVES_ONLY, config.isRecommendedSupportedGroups());
dtlsConfig.set(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, config.isRecommendedCiphers());
dtlsConfig.setServerOnly(true);
dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(config.isRecommendedCiphers());
/* Create credentials */ /* Create credentials */
this.setServerWithCredentials(builder, dtlsConfig); this.setServerWithCredentials(builder, dtlsConfig);
@ -144,12 +148,12 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
builder.setCertificateChain(sslCredentials.getCertificateChain()); builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier); dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
builder.setAuthorizer(authorizer); builder.setAuthorizer(authorizer);
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES); dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, RPK_OR_X509_CIPHER_SUITES);
} else { } else {
/* by default trust all */ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]); builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for LWM2MServer"); log.info("Unable to load X509 files for LWM2MServer");
dtlsConfig.setSupportedCipherSuites(PSK_CIPHER_SUITES); dtlsConfig.setAsList(DtlsConfig.DTLS_CIPHER_SUITES, PSK_CIPHER_SUITES);
} }
} }

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

@ -15,27 +15,30 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.config.CoapConfig;
import org.eclipse.californium.core.network.config.NetworkConfigDefaults; import org.eclipse.californium.elements.config.Configuration;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME; import java.util.Properties;
import java.util.concurrent.TimeUnit;
import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS;
public class LwM2MNetworkConfig { public class LwM2MNetworkConfig {
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) { public static Configuration getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) {
NetworkConfig coapConfig = new NetworkConfig(); Configuration coapConfig = new Configuration();
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT, serverPortNoSec); coapConfig.set(CoapConfig.COAP_PORT, serverPortNoSec);
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT, serverSecurePort); coapConfig.set(CoapConfig.COAP_SECURE_PORT, serverSecurePort);
/** /**
Example:Property for large packet: Example:Property for large packet:
#NetworkConfig config = new NetworkConfig(); #NetworkConfig config = new NetworkConfig();
#config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32); #config.setInt(CoapConfig.MAX_MESSAGE_SIZE,32);
#config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32); #config.setInt(CoapConfig.PREFERRED_BLOCK_SIZE,32);
#config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048); #config.setInt(CoapConfig.MAX_RESOURCE_BODY_SIZE,2048);
#config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3); #config.setInt(CoapConfig.MAX_RETRANSMIT,3);
#config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000); #config.setInt(CoapConfig.MAX_TRANSMIT_WAIT,120000);
*/ */
/** /**
@ -46,14 +49,14 @@ public class LwM2MNetworkConfig {
CoAP client will try to use block mode CoAP client will try to use block mode
or adapt the block size when receiving a 4.13 Entity too large response code or adapt the block size when receiving a 4.13 Entity too large response code
*/ */
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); coapConfig.set(CoapConfig.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
/** /**
Property to indicate if the response should always include the Block2 option \ Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet. when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required. - value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required. - value of true indicate that the server will response with block2 option event if no further blocks are required.
*/ */
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); coapConfig.set(CoapConfig.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
/** /**
* The maximum amount of time (in milliseconds) allowed between * The maximum amount of time (in milliseconds) allowed between
* transfers of individual blocks in a blockwise transfer before the * transfers of individual blocks in a blockwise transfer before the
@ -62,7 +65,7 @@ public class LwM2MNetworkConfig {
* The default value of this property is * The default value of this property is
* {@link NetworkConfigDefaults#DEFAULT_BLOCKWISE_STATUS_LIFETIME} = 5 * 60 * 1000; // 5 mins [ms]. * {@link NetworkConfigDefaults#DEFAULT_BLOCKWISE_STATUS_LIFETIME} = 5 * 60 * 1000; // 5 mins [ms].
*/ */
coapConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME); coapConfig.set(CoapConfig.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS, TimeUnit.SECONDS);
/** /**
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13 !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted The maximum size of a resource body (in bytes) that will be accepted
@ -77,7 +80,7 @@ public class LwM2MNetworkConfig {
The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192 The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
A value of {@code 0} turns off transparent handling of blockwise transfers altogether. A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
*/ */
coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); coapConfig.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
/** /**
The default DTLS response matcher. The default DTLS response matcher.
Supported values are STRICT, RELAXED, or PRINCIPAL. Supported values are STRICT, RELAXED, or PRINCIPAL.
@ -88,13 +91,13 @@ public class LwM2MNetworkConfig {
– true with address check, (STRICT, UDP) - if port Registration of client is changed - it is bad – true with address check, (STRICT, UDP) - if port Registration of client is changed - it is bad
- false, without - false, without
*/ */
coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); coapConfig.set(CoapConfig.RESPONSE_MATCHING, CoapConfig.MatcherMode.RELAXED);
/** /**
https://tools.ietf.org/html/rfc7959#section-2.9.3 https://tools.ietf.org/html/rfc7959#section-2.9.3
The block size (number of bytes) to use when doing a blockwise transfer. \ The block size (number of bytes) to use when doing a blockwise transfer. \
This value serves as the upper limit for block size in blockwise transfers This value serves as the upper limit for block size in blockwise transfers
*/ */
coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); coapConfig.set(CoapConfig.PREFERRED_BLOCK_SIZE, 1024);
/** /**
The maximum payload size (in bytes) that can be transferred in a The maximum payload size (in bytes) that can be transferred in a
single message, i.e. without requiring a blockwise transfer. single message, i.e. without requiring a blockwise transfer.
@ -102,12 +105,14 @@ public class LwM2MNetworkConfig {
In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
DEFAULT_VALUE = 1024 DEFAULT_VALUE = 1024
*/ */
coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); coapConfig.set(CoapConfig.MAX_MESSAGE_SIZE, 1024);
coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10); coapConfig.set(CoapConfig.MAX_RETRANSMIT, 10);
if (!CollectionUtils.isEmpty(config.getNetworkConfig())) { if (!CollectionUtils.isEmpty(config.getNetworkConfig())) {
config.getNetworkConfig().forEach(p -> coapConfig.setString(p.getKey(), p.getValue())); Properties networkProps = new Properties();
config.getNetworkConfig().forEach(p -> networkProps.put(p.getKey(), p.getValue()));
coapConfig.add(networkProps);
} }
return coapConfig; return coapConfig;

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

@ -16,7 +16,10 @@
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.observation.CompositeObservation;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.observation.SingleObservation;
import org.eclipse.leshan.core.response.ObserveCompositeResponse;
import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.core.response.ObserveResponse;
import org.eclipse.leshan.server.observation.ObservationListener; import org.eclipse.leshan.server.observation.ObservationListener;
import org.eclipse.leshan.server.queue.PresenceListener; import org.eclipse.leshan.server.queue.PresenceListener;
@ -86,26 +89,34 @@ public class LwM2mServerListener {
@Override @Override
public void cancelled(Observation observation) { public void cancelled(Observation observation) {
log.trace("Canceled Observation {}.", observation.getPath()); //TODO: should be able to use CompositeObservation
log.trace("Canceled Observation {}.", ((SingleObservation)observation).getPath());
} }
@Override @Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) { public void onResponse(SingleObservation observation, Registration registration, ObserveResponse response) {
if (registration != null) { if (registration != null) {
service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response); service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response);
} }
} }
@Override
public void onResponse(CompositeObservation observation, Registration registration, ObserveCompositeResponse response) {
throw new RuntimeException("Not implemented yet!");
}
@Override @Override
public void onError(Observation observation, Registration registration, Exception error) { public void onError(Observation observation, Registration registration, Exception error) {
if (error != null) { if (error != null) {
log.debug("Unable to handle notification of [{}:{}] [{}]", observation.getRegistrationId(), observation.getPath(), error.getMessage()); //TODO: should be able to use CompositeObservation
log.debug("Unable to handle notification of [{}:{}] [{}]", observation.getRegistrationId(), ((SingleObservation)observation).getPath(), error.getMessage());
} }
} }
@Override @Override
public void newObservation(Observation observation, Registration registration) { public void newObservation(Observation observation, Registration registration) {
log.trace("Successful start newObservation {}.", observation.getPath()); //TODO: should be able to use CompositeObservation
log.trace("Successful start newObservation {}.", ((SingleObservation)observation).getPath());
} }
}; };
} }

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

@ -56,7 +56,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
if (relation == null || relation.isCanceled()) { if (relation == null || relation.isCanceled()) {
return; // because request did not try to establish a relation return; // because request did not try to establish a relation
} }
if (CoAP.ResponseCode.isSuccess(response.getCode())) { if (response.getCode().isSuccess()) {
if (!relation.isEstablished()) { if (!relation.isEstablished()) {
relation.setEstablished(); relation.setEstablished();

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

@ -39,7 +39,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion; import org.thingsboard.server.transport.lwm2m.config.TbLwM2mVersion;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
@ -423,14 +423,14 @@ public class LwM2mClient implements Serializable {
if (registration == null) { if (registration == null) {
return ContentFormat.DEFAULT; return ContentFormat.DEFAULT;
} else{ } else{
return LwM2mVersion.fromVersion(registration.getLwM2mVersion()).getContentFormat(); return TbLwM2mVersion.fromVersion(registration.getLwM2mVersion()).getContentFormat();
} }
} }
static private Set<ContentFormat> clientSupportContentFormat(Registration registration) { static private Set<ContentFormat> clientSupportContentFormat(Registration registration) {
Set<ContentFormat> contentFormats = new HashSet<>(); Set<ContentFormat> contentFormats = new HashSet<>();
contentFormats.add(ContentFormat.DEFAULT); contentFormats.add(ContentFormat.DEFAULT);
String code = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUrl().equals("/")).findFirst().get().getAttributes().get("ct"); String code = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUriReference().equals("/")).findFirst().get().getLinkParams().get("ct").getUnquoted();
if (code != null) { if (code != null) {
Set<ContentFormat> codes = Stream.of(code.replaceAll("\"", "").split(" ", -1)) Set<ContentFormat> codes = Stream.of(code.replaceAll("\"", "").split(" ", -1))
.map(String::trim) .map(String::trim)

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java

@ -393,9 +393,9 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
public Set<String> getSupportedIdVerInClient(LwM2mClient client) { public Set<String> getSupportedIdVerInClient(LwM2mClient client) {
Set<String> clientObjects = ConcurrentHashMap.newKeySet(); Set<String> clientObjects = ConcurrentHashMap.newKeySet();
Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> { Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> {
LwM2mPath pathIds = new LwM2mPath(link.getUrl()); LwM2mPath pathIds = new LwM2mPath(link.getUriReference());
if (!pathIds.isRoot()) { if (!pathIds.isRoot()) {
clientObjects.add(convertObjectIdToVersionedId(link.getUrl(), client.getRegistration())); clientObjects.add(convertObjectIdToVersionedId(link.getUriReference(), client.getRegistration()));
} }
}); });
return (clientObjects.size() > 0) ? clientObjects : null; return (clientObjects.size() > 0) ? clientObjects : null;

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

@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.LwM2m; import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.attributes.AttributeSet;
@ -29,6 +29,7 @@ import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.observation.SingleObservation;
import org.eclipse.leshan.core.request.CompositeDownlinkRequest; import org.eclipse.leshan.core.request.CompositeDownlinkRequest;
import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.CreateRequest; import org.eclipse.leshan.core.request.CreateRequest;
@ -160,7 +161,8 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
validateVersionedId(client, request); validateVersionedId(client, request);
LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); LwM2mPath resultIds = new LwM2mPath(request.getObjectId());
Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration()); Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration());
if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { //TODO: should be able to use CompositeObservation
if (observations.stream().noneMatch(observation -> ((SingleObservation)observation).getPath().equals(resultIds))) {
ObserveRequest downlink; ObserveRequest downlink;
ContentFormat contentFormat = getReadRequestContentFormat(client, request, modelProvider); ContentFormat contentFormat = getReadRequestContentFormat(client, request, modelProvider);
if (resultIds.isResource()) { if (resultIds.isResource()) {
@ -183,7 +185,8 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override @Override
public void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback<TbLwM2MObserveAllRequest, Set<String>> callback) { public void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback<TbLwM2MObserveAllRequest, Set<String>> callback) {
Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration()); Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration());
Set<String> paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); //TODO: should be able to use CompositeObservation
Set<String> paths = observations.stream().map(observation -> ((SingleObservation)observation).getPath().toString()).collect(Collectors.toUnmodifiableSet());
callback.onSuccess(request, paths); callback.onSuccess(request, paths);
} }
@ -620,9 +623,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} }
private static ContentFormat getContentFormatForComplex(LwM2mClient client) { private static ContentFormat getContentFormatForComplex(LwM2mClient client) {
if (LwM2m.Version.V1_0.equals(client.getRegistration().getLwM2mVersion())) { if (LwM2m.LwM2mVersion.V1_0.equals(client.getRegistration().getLwM2mVersion())) {
return ContentFormat.TLV; return ContentFormat.TLV;
} else if (LwM2m.Version.V1_1.equals(client.getRegistration().getLwM2mVersion())) { } else if (LwM2m.LwM2mVersion.V1_1.equals(client.getRegistration().getLwM2mVersion())) {
ContentFormat result = findFirst(client.getClientSupportContentFormats(), null, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON, ContentFormat.TLV, ContentFormat.JSON); ContentFormat result = findFirst(client.getClientSupportContentFormats(), null, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON, ContentFormat.TLV, ContentFormat.JSON);
if (result != null) { if (result != null) {
return result; return result;

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

@ -15,7 +15,7 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.downlink; package org.thingsboard.server.transport.lwm2m.server.downlink;
import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.CreateRequest; import org.eclipse.leshan.core.request.CreateRequest;
import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.request.DeleteRequest;

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

@ -15,7 +15,8 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.rpc; package org.thingsboard.server.transport.lwm2m.server.rpc;
import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.link.DefaultLinkSerializer;
import org.eclipse.leshan.core.link.LinkSerializer;
import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.request.DiscoverRequest;
import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.DiscoverResponse;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
@ -27,12 +28,14 @@ import java.util.Optional;
public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback<DiscoverRequest, DiscoverResponse> { public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback<DiscoverRequest, DiscoverResponse> {
private final LinkSerializer serializer = new DefaultLinkSerializer();
public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback) { public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback) {
super(transportService, client, requestMsg, callback); super(transportService, client, requestMsg, callback);
} }
protected Optional<String> serializeSuccessfulResponse(DiscoverResponse response) { protected Optional<String> serializeSuccessfulResponse(DiscoverResponse response) {
return Optional.of(Link.serialize(response.getObjectLinks())); return Optional.of(serializer.serialize(response.getObjectLinks()));
} }
} }

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

@ -22,10 +22,11 @@ import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.Startable; import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable; import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.observation.SingleObservation;
import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.eclipse.leshan.core.util.Validate; import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.californium.observation.ObserveUtil; import org.eclipse.leshan.core.californium.ObserveUtil;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.redis.RedisRegistrationStore; import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.serialization.IdentitySerDes; import org.eclipse.leshan.server.redis.serialization.IdentitySerDes;
@ -455,7 +456,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
// cancel existing observations for the same path and registration id. // cancel existing observations for the same path and registration id.
for (Observation obs : getObservations(connection, registrationId)) { for (Observation obs : getObservations(connection, registrationId)) {
if (observation.getPath().equals(obs.getPath()) //TODO: should be able to use CompositeObservation
if (((SingleObservation)observation).getPath().equals(((SingleObservation)obs).getPath())
&& !Arrays.equals(observation.getId(), obs.getId())) { && !Arrays.equals(observation.getId(), obs.getId())) {
removed.add(obs); removed.add(obs);
unsafeRemoveObservation(connection, registrationId, obs.getId()); unsafeRemoveObservation(connection, registrationId, obs.getId());

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

@ -30,7 +30,6 @@ import org.eclipse.leshan.core.node.LwM2mMultipleResource;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.SimpleDownlinkRequest; import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest;
@ -44,7 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion; import org.thingsboard.server.transport.lwm2m.config.TbLwM2mVersion;
import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
@ -236,7 +235,7 @@ public class LwM2MTransportUtil {
public static String convertObjectIdToVersionedId(String path, Registration registration) { public static String convertObjectIdToVersionedId(String path, Registration registration) {
String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId()); String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId());
ver = ver != null ? ver : LwM2mVersion.VERSION_1_0.getVersion().toString(); ver = ver != null ? ver : TbLwM2mVersion.VERSION_1_0.getVersion().toString();
try { try {
String[] keyArray = path.split(LWM2M_SEPARATOR_PATH); String[] keyArray = path.split(LWM2M_SEPARATOR_PATH);
if (keyArray.length > 1) { if (keyArray.length > 1) {

2
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java

@ -122,7 +122,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg); return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg);
} }
@Override @Override
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg, String topic) { public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg, String topic) {
return Optional.of(createMqttPublishMsg(ctx, topic, JsonConverter.toJson(notificationMsg))); return Optional.of(createMqttPublishMsg(ctx, topic, JsonConverter.toJson(notificationMsg)));

14
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java

@ -313,16 +313,18 @@ public class JsonConverter {
return result; return result;
} }
public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg public static JsonObject getJsonObjectForGateway(
responseMsg) { String deviceName,
TransportProtos.GetAttributeResponseMsg responseMsg
) {
JsonObject result = new JsonObject(); JsonObject result = new JsonObject();
result.addProperty("id", responseMsg.getRequestId()); result.addProperty("id", responseMsg.getRequestId());
result.addProperty(DEVICE_PROPERTY, deviceName); result.addProperty(DEVICE_PROPERTY, deviceName);
if (responseMsg.getClientAttributeListCount() > 0) { if (responseMsg.getClientAttributeListCount() > 0) {
addValues(result, responseMsg.getClientAttributeListList()); addValues(result, responseMsg.getClientAttributeListList(), responseMsg.getIsMultipleAttributesRequest());
} }
if (responseMsg.getSharedAttributeListCount() > 0) { if (responseMsg.getSharedAttributeListCount() > 0) {
addValues(result, responseMsg.getSharedAttributeListList()); addValues(result, responseMsg.getSharedAttributeListList(), responseMsg.getIsMultipleAttributesRequest());
} }
return result; return result;
} }
@ -335,8 +337,8 @@ public class JsonConverter {
return result; return result;
} }
private static void addValues(JsonObject result, List<TransportProtos.TsKvProto> kvList) { private static void addValues(JsonObject result, List<TransportProtos.TsKvProto> kvList, boolean multipleAttrKeysRequested) {
if (kvList.size() == 1) { if (kvList.size() == 1 && !multipleAttrKeysRequested) {
addValueToJson(result, "value", kvList.get(0).getKv()); addValueToJson(result, "value", kvList.get(0).getKv());
} else { } else {
JsonObject values; JsonObject values;

2
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java

@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.cassandra.cql3.Json; import org.apache.cassandra.cql3.Json;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
@ -69,6 +70,7 @@ public abstract class AbstractContainerTest {
protected static String TB_TOKEN; protected static String TB_TOKEN;
protected static RestClient restClient; protected static RestClient restClient;
protected ObjectMapper mapper = new ObjectMapper(); protected ObjectMapper mapper = new ObjectMapper();
protected JsonParser jsonParser = new JsonParser();
@BeforeClass @BeforeClass
public static void before() throws Exception { public static void before() throws Exception {

70
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java

@ -20,6 +20,7 @@ import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -151,6 +152,75 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
Assert.assertTrue(verify(actualLatestTelemetry, "attr4", Long.toString(73))); Assert.assertTrue(verify(actualLatestTelemetry, "attr4", Long.toString(73)));
} }
@Test
public void responseDataOnAttributesRequestCheck() throws Exception {
Optional<DeviceCredentials> createdDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(createdDevice.getId());
Assert.assertTrue(createdDeviceCredentials.isPresent());
JsonObject sharedAttributes = new JsonObject();
sharedAttributes.addProperty("attr1", "value1");
sharedAttributes.addProperty("attr2", true);
sharedAttributes.addProperty("attr3", 42.0);
sharedAttributes.addProperty("attr4", 73);
mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
ResponseEntity sharedAttributesResponse = restClient.getRestTemplate()
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
createdDevice.getId());
Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
var event = listener.getEvents().poll(10, TimeUnit.SECONDS);
JsonObject requestData = new JsonObject();
requestData.addProperty("id", 1);
requestData.addProperty("device", createdDevice.getName());
requestData.addProperty("client", false);
requestData.addProperty("key", "attr1");
mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get();
event = listener.getEvents().poll(10, TimeUnit.SECONDS);
JsonObject responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject();
Assert.assertTrue(responseData.has("value"));
Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("value").getAsString());
requestData = new JsonObject();
requestData.addProperty("id", 1);
requestData.addProperty("device", createdDevice.getName());
requestData.addProperty("client", false);
JsonArray keys = new JsonArray();
keys.add("attr1");
keys.add("attr2");
requestData.add("keys", keys);
mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get();
event = listener.getEvents().poll(10, TimeUnit.SECONDS);
responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject();
Assert.assertTrue(responseData.has("values"));
Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("values").getAsJsonObject().get("attr1").getAsString());
Assert.assertEquals(sharedAttributes.get("attr2").getAsString(), responseData.get("values").getAsJsonObject().get("attr2").getAsString());
requestData = new JsonObject();
requestData.addProperty("id", 1);
requestData.addProperty("device", createdDevice.getName());
requestData.addProperty("client", false);
keys = new JsonArray();
keys.add("attr1");
keys.add("undefined");
requestData.add("keys", keys);
mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get();
event = listener.getEvents().poll(10, TimeUnit.SECONDS);
responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject();
Assert.assertTrue(responseData.has("values"));
Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("values").getAsJsonObject().get("attr1").getAsString());
Assert.assertEquals(1, responseData.get("values").getAsJsonObject().entrySet().size());
}
@Test @Test
public void requestAttributeValuesFromServer() throws Exception { public void requestAttributeValuesFromServer() throws Exception {
WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS);

4
pom.xml

@ -68,8 +68,8 @@
<jackson.version>2.12.1</jackson.version> <jackson.version>2.12.1</jackson.version>
<fasterxml-classmate.version>1.3.4</fasterxml-classmate.version> <fasterxml-classmate.version>1.3.4</fasterxml-classmate.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version> <json-schema-validator.version>2.2.6</json-schema-validator.version>
<californium.version>2.6.1</californium.version> <californium.version>3.0.0</californium.version>
<leshan.version>2.0.0-M4</leshan.version> <leshan.version>2.0.0-M5</leshan.version>
<gson.version>2.6.2</gson.version> <gson.version>2.6.2</gson.version>
<freemarker.version>2.3.30</freemarker.version> <freemarker.version>2.3.30</freemarker.version>
<mail.version>1.6.2</mail.version> <mail.version>1.6.2</mail.version>

67
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java

@ -229,18 +229,10 @@ class AlarmRuleState {
long repeatingTimes = 0; long repeatingTimes = 0;
AlarmConditionSpec alarmConditionSpec = getSpec(); AlarmConditionSpec alarmConditionSpec = getSpec();
AlarmConditionSpecType specType = alarmConditionSpec.getType(); AlarmConditionSpecType specType = alarmConditionSpec.getType();
if(specType.equals(AlarmConditionSpecType.REPEATING)) { if (specType.equals(AlarmConditionSpecType.REPEATING)) {
RepeatingAlarmConditionSpec repeating = (RepeatingAlarmConditionSpec) spec; RepeatingAlarmConditionSpec repeating = (RepeatingAlarmConditionSpec) spec;
repeatingTimes = repeating.getPredicate().getDefaultValue(); repeatingTimes = resolveDynamicValue(data, repeating.getPredicate());
if (repeating.getPredicate().getDynamicValue() != null &&
repeating.getPredicate().getDynamicValue().getSourceAttribute() != null) {
EntityKeyValue repeatingKeyValue = getDynamicPredicateValue(data, repeating.getPredicate().getDynamicValue());
if (repeatingKeyValue != null) {
repeatingTimes = repeatingKeyValue.getLngValue();
}
}
} }
return repeatingTimes; return repeatingTimes;
} }
@ -249,22 +241,33 @@ class AlarmRuleState {
long durationTimeInMs = 0; long durationTimeInMs = 0;
AlarmConditionSpec alarmConditionSpec = getSpec(); AlarmConditionSpec alarmConditionSpec = getSpec();
AlarmConditionSpecType specType = alarmConditionSpec.getType(); AlarmConditionSpecType specType = alarmConditionSpec.getType();
if(specType.equals(AlarmConditionSpecType.DURATION)) { if (specType.equals(AlarmConditionSpecType.DURATION)) {
DurationAlarmConditionSpec duration = (DurationAlarmConditionSpec) spec; DurationAlarmConditionSpec duration = (DurationAlarmConditionSpec) spec;
TimeUnit timeUnit = duration.getUnit(); TimeUnit timeUnit = duration.getUnit();
durationTimeInMs = timeUnit.toMillis(duration.getPredicate().getDefaultValue()); durationTimeInMs = timeUnit.toMillis(resolveDynamicValue(data, duration.getPredicate()));
}
return durationTimeInMs;
}
if (duration.getPredicate().getDynamicValue() != null && private Long resolveDynamicValue(DataSnapshot data, FilterPredicateValue<? extends Number> predicate) {
duration.getPredicate().getDynamicValue().getSourceAttribute() != null) { DynamicValue<?> dynamicValue = predicate.getDynamicValue();
EntityKeyValue durationKeyValue = getDynamicPredicateValue(data, duration.getPredicate().getDynamicValue()); Long defaultValue = predicate.getDefaultValue().longValue();
if (durationKeyValue != null) { if (dynamicValue == null || dynamicValue.getSourceAttribute() == null) {
durationTimeInMs = timeUnit.toMillis(durationKeyValue.getLngValue()); return defaultValue;
}
}
} }
return durationTimeInMs; EntityKeyValue keyValue = getDynamicPredicateValue(data, dynamicValue);
if (keyValue == null) {
return defaultValue;
}
var longValue = getLongValue(keyValue);
if (longValue == null) {
String sourceAttribute = dynamicValue.getSourceAttribute();
throw new NumericParseException(String.format("Could not convert attribute '%s' with value '%s' to numeric value!", sourceAttribute, getStrValue(keyValue)));
}
return longValue;
} }
public AlarmEvalResult eval(long ts, DataSnapshot dataSnapshot) { public AlarmEvalResult eval(long ts, DataSnapshot dataSnapshot) {
@ -545,4 +548,28 @@ class AlarmRuleState {
} }
} }
private static Long getLongValue(EntityKeyValue ekv) {
switch (ekv.getDataType()) {
case LONG:
return ekv.getLngValue();
case DOUBLE:
return ekv.getDblValue() != null ? ekv.getDblValue().longValue() : null;
case BOOLEAN:
return ekv.getBoolValue() != null ? (ekv.getBoolValue() ? 1 : 0L) : null;
case STRING:
try {
return Long.parseLong(ekv.getStrValue());
} catch (RuntimeException e) {
return null;
}
case JSON:
try {
return Long.parseLong(ekv.getJsonValue());
} catch (RuntimeException e) {
return null;
}
default:
return null;
}
}
} }

13
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java

@ -51,6 +51,7 @@ import java.util.function.BiFunction;
@Slf4j @Slf4j
class AlarmState { class AlarmState {
public static final String ERROR_MSG = "Failed to process alarm rule for Device [%s]: %s";
private final ProfileState deviceProfile; private final ProfileState deviceProfile;
private final EntityId originator; private final EntityId originator;
private DeviceProfileAlarm alarmDefinition; private DeviceProfileAlarm alarmDefinition;
@ -75,12 +76,20 @@ class AlarmState {
lastMsgMetaData = msg.getMetaData(); lastMsgMetaData = msg.getMetaData();
lastMsgQueueName = msg.getQueueName(); lastMsgQueueName = msg.getQueueName();
this.dataSnapshot = data; this.dataSnapshot = data;
return createOrClearAlarms(ctx, msg, data, update, AlarmRuleState::eval); try {
return createOrClearAlarms(ctx, msg, data, update, AlarmRuleState::eval);
} catch (NumericParseException e) {
throw new RuntimeException(String.format(ERROR_MSG, originator.getId().toString(), e.getMessage()));
}
} }
public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException { public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException {
initCurrentAlarm(ctx); initCurrentAlarm(ctx);
return createOrClearAlarms(ctx, null, ts, null, (alarmState, tsParam) -> alarmState.eval(tsParam, dataSnapshot)); try {
return createOrClearAlarms(ctx, null, ts, null, (alarmState, tsParam) -> alarmState.eval(tsParam, dataSnapshot));
} catch (NumericParseException e) {
throw new RuntimeException(String.format(ERROR_MSG, originator.getId().toString(), e.getMessage()));
}
} }
public <T> boolean createOrClearAlarms(TbContext ctx, TbMsg msg, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) { public <T> boolean createOrClearAlarms(TbContext ctx, TbMsg msg, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) {

22
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/NumericParseException.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2022 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.rule.engine.profile;
public class NumericParseException extends RuntimeException {
public NumericParseException(String message) {
super(message);
}
}

15
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java

@ -130,7 +130,7 @@ public class TbAlarmNodeTest {
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null));
doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class));
long ts = msg.getTs();
node.onMsg(ctx, msg); node.onMsg(ctx, msg);
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture()); verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
@ -152,6 +152,8 @@ public class TbAlarmNodeTest {
Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class);
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.startTs(ts)
.endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK) .status(ACTIVE_UNACK)
@ -192,7 +194,7 @@ public class TbAlarmNodeTest {
initWithCreateAlarmScript(); initWithCreateAlarmScript();
metaData.putValue("key", "value"); metaData.putValue("key", "value");
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
long ts = msg.getTs();
Alarm clearedAlarm = Alarm.builder().status(CLEARED_ACK).build(); Alarm clearedAlarm = Alarm.builder().status(CLEARED_ACK).build();
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
@ -222,6 +224,8 @@ public class TbAlarmNodeTest {
Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class);
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.startTs(ts)
.endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK) .status(ACTIVE_UNACK)
@ -407,7 +411,7 @@ public class TbAlarmNodeTest {
String rawJson = "{\"alarmSeverity\": \"WARNING\", \"passed\": 5}"; String rawJson = "{\"alarmSeverity\": \"WARNING\", \"passed\": 5}";
metaData.putValue("key", "value"); metaData.putValue("key", "value");
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
long ts = msg.getTs();
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null));
doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class));
@ -433,6 +437,8 @@ public class TbAlarmNodeTest {
Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class);
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.startTs(ts)
.endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK) .status(ACTIVE_UNACK)
@ -467,6 +473,7 @@ public class TbAlarmNodeTest {
metaData.putValue("alarmSeverity", "WARNING"); metaData.putValue("alarmSeverity", "WARNING");
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
long ts = msg.getTs();
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null));
@ -492,6 +499,8 @@ public class TbAlarmNodeTest {
Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class);
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.startTs(ts)
.endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK) .status(ACTIVE_UNACK)

66
ui-ngx/generate-types.js

@ -0,0 +1,66 @@
/*
* Copyright © 2016-2022 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.
*/
const child_process = require("child_process");
const fs = require('fs');
const path = require('path');
const typeDir = './target/types';
const srcDir = typeDir + '/src';
const moduleMapPath = "src/app/modules/common/modules-map.ts";
console.log(`Remove directory: ${typeDir}`);
try {
fs.rmSync(typeDir, {recursive: true, force: true,});
} catch (err) {
console.error(`Remove directory error: ${err}`);
}
const cliCommand = `./node_modules/.bin/ngc --p src/tsconfig.app.json --declaration --outDir ${srcDir}`;
console.log(cliCommand);
try {
child_process.execSync(cliCommand);
} catch (err) {
console.error("Build types", err);
process.exit(1);
}
function fromDir(startPath, filter, callback) {
if (!fs.existsSync(startPath)) {
console.log("not dirs", startPath);
process.exit(1);
}
const files = fs.readdirSync(startPath);
for (let i = 0; i < files.length; i++) {
const filename = path.join(startPath, files[i]);
const stat = fs.lstatSync(filename);
if (stat.isDirectory()) {
fromDir(filename, filter, callback);
} else if (filter.test(filename)) {
callback(filename)
}
}
}
fromDir(srcDir, /(\.js|\.js\.map)$/, function (filename) {
try {
fs.rmSync(filename);
} catch (err) {
console.error(`Remove file error ${filename}: ${err}`);
}
});
fs.cpSync(moduleMapPath, `${typeDir}/${moduleMapPath}`);

1
ui-ngx/package.json

@ -6,6 +6,7 @@
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open",
"build": "ng build", "build": "ng build",
"build:prod": "ng build --configuration production --vendor-chunk", "build:prod": "ng build --configuration production --vendor-chunk",
"build:types": "node generate-types.js",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",
"e2e": "ng e2e", "e2e": "ng e2e",

Loading…
Cancel
Save