Browse Source

Merge pull request #4707 from thingsboard/lwm2m-refactoring

LwM2M Refactoring
pull/4712/head
Andrew Shvayka 5 years ago
committed by GitHub
parent
commit
e86af4b677
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  2. 47
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  3. 69
      application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java
  4. 48
      application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java
  5. 121
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  6. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java
  7. BIN
      application/src/test/resources/lwm2m/credentials/clientKeyStore.jks
  8. BIN
      application/src/test/resources/lwm2m/credentials/serverKeyStore.jks
  9. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  10. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java
  11. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java
  12. 12
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java
  13. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  14. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MAuthorizer.java
  15. 75
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  16. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java
  17. 375
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
  18. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  19. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  20. 7
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
  21. 88
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  22. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
  23. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java
  24. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientState.java
  25. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientStateException.java
  26. 70
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  27. 19
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java
  28. 189
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  29. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java
  30. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java
  31. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbEditableSecurityStore.java
  32. 130
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java
  33. 54
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java
  34. 141
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java
  35. 82
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java
  36. 14
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java
  37. 25
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbSecurityStore.java
  38. 12
      pom.xml

8
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -72,15 +72,15 @@ public class DefaultTbResourceService implements TbResourceService {
if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) {
try {
List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
if (!objectModels.isEmpty()) {
ObjectModel objectModel = objectModels.get(0);
String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.getVersion();
String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version;
String name = objectModel.name;
resource.setResourceKey(resourceKey);
if (resource.getId() == null) {
resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.getVersion());
resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version);
}
resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name);
} else {
@ -176,7 +176,7 @@ public class DefaultTbResourceService implements TbResourceService {
try {
DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
if (objectModels.size() == 0) {
return null;
} else {

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

@ -60,6 +60,53 @@ import java.util.concurrent.ScheduledExecutorService;
@DaoSqlTest
public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"U\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5686,\n" +
" \"serverId\": 123,\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
protected DeviceProfile deviceProfile;
protected ScheduledExecutorService executor;
protected TbTestWebSocketClient wsClient;

69
application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java

@ -22,6 +22,7 @@ import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
@ -36,7 +37,6 @@ import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import java.util.Collections;
import java.util.List;
@ -46,60 +46,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"UQ\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5685,\n" +
" \"serverId\": 123,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
private final int port = 5685;
private final Security security = noSec("coap://localhost:" + port, 123);
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port));
private final int PORT = 5685;
private final Security SECURITY = noSec("coap://localhost:" + PORT, 123);
private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
private final String ENDPOINT = "deviceAEndpoint";
@NotNull
private Device createDevice(String deviceAEndpoint) throws Exception {
private Device createDevice() throws Exception {
Device device = new Device();
device.setName("Device A");
device.setDeviceProfileId(deviceProfile.getId());
@ -114,7 +67,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
LwM2MCredentials noSecCredentials = new LwM2MCredentials();
NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint(deviceAEndpoint);
clientCredentials.setEndpoint(ENDPOINT);
noSecCredentials.setClient(clientCredentials);
deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
@ -125,9 +78,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
public void testConnectAndObserveTelemetry() throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION);
String deviceAEndpoint = "deviceAEndpoint";
Device device = createDevice(deviceAEndpoint);
Device device = createDevice();
SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId());
@ -144,8 +95,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
wsClient.waitForReply();
wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint);
client.init(security, coapConfig);
LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT);
client.init(SECURITY, COAP_CONFIG);
String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);

48
application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java

@ -47,54 +47,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"UQ\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5686,\n" +
" \"serverId\": 123,\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
private final int port = 5686;
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port));
private final String endpoint = "deviceAEndpoint";

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

@ -17,40 +17,37 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.elements.Connector;
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.dtls.ClientHandshaker;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.Handshaker;
import org.eclipse.californium.scandium.dtls.ResumingClientHandshaker;
import org.eclipse.californium.scandium.dtls.ResumingServerHandshaker;
import org.eclipse.californium.scandium.dtls.ServerHandshaker;
import org.eclipse.californium.scandium.dtls.SessionAdapter;
import org.eclipse.leshan.client.californium.LeshanClient;
import org.eclipse.leshan.client.californium.LeshanClientBuilder;
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.LwM2mId;
import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.californium.DefaultEndpointFactory;
import org.eclipse.leshan.core.californium.EndpointFactory;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectLoader;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.StaticModel;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.DeregisterRequest;
import org.eclipse.leshan.core.request.RegisterRequest;
import org.eclipse.leshan.core.request.UpdateRequest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
@ -67,7 +64,7 @@ public class LwM2MTestClient {
private final String endpoint;
private LeshanClient client;
public void init(Security security, NetworkConfig coapConfig) {
public void init(Security security, NetworkConfig coapConfig) throws InvalidDDFFileException, IOException {
String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"};
List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) {
@ -76,82 +73,51 @@ public class LwM2MTestClient {
LwM2mModel model = new StaticModel(models);
ObjectsInitializer initializer = new ObjectsInitializer(model);
initializer.setInstancesForObject(SECURITY, security);
initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false));
initializer.setInstancesForObject(SERVER, new Server(123, 300));
initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice());
initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedCipherSuitesOnly(true);
dtlsConfig.setClientOnly();
DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory();
engineFactory.setReconnectOnUpdate(false);
engineFactory.setResumeOnConnect(true);
DefaultEndpointFactory endpointFactory = new DefaultEndpointFactory(endpoint) {
EndpointFactory endpointFactory = new EndpointFactory() {
@Override
protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) {
return new DTLSConnector(dtlsConfig) {
@Override
protected void onInitializeHandshaker(Handshaker handshaker) {
handshaker.addSessionListener(new SessionAdapter() {
@Override
public void handshakeStarted(Handshaker handshaker) throws HandshakeException {
if (handshaker instanceof ServerHandshaker) {
log.info("DTLS Full Handshake initiated by server : STARTED ...");
} else if (handshaker instanceof ResumingServerHandshaker) {
log.info("DTLS abbreviated Handshake initiated by server : STARTED ...");
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : STARTED ...");
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : STARTED ...");
}
}
@Override
public void sessionEstablished(Handshaker handshaker, DTLSSession establishedSession)
throws HandshakeException {
if (handshaker instanceof ServerHandshaker) {
log.info("DTLS Full Handshake initiated by server : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ResumingServerHandshaker) {
log.info("DTLS abbreviated Handshake initiated by server : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
}
}
@Override
public void handshakeFailed(Handshaker handshaker, Throwable error) {
/** get cause */
String cause;
if (error != null) {
if (error.getMessage() != null) {
cause = error.getMessage();
} else {
cause = error.getClass().getName();
}
} else {
cause = "unknown cause";
}
if (handshaker instanceof ServerHandshaker) {
log.info("DTLS Full Handshake initiated by server : FAILED [{}]", cause);
} else if (handshaker instanceof ResumingServerHandshaker) {
log.info("DTLS abbreviated Handshake initiated by server : FAILED [{}]", cause);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : FAILED [{}]", cause);
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : FAILED [{}]", cause);
}
}
});
}
};
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);
// tricks to be able to change psk information on the fly
// AdvancedPskStore pskStore = dtlsConfig.getAdvancedPskStore();
// if (pskStore != null) {
// PskPublicInformation identity = pskStore.getIdentity(null, null);
// SecretKey key = pskStore
// .requestPskSecretResult(ConnectionId.EMPTY, null, identity, null, null, null).getSecret();
// singlePSKStore = new SinglePSKStore(identity, key);
// dtlsConfigBuilder.setAdvancedPskStore(singlePSKStore);
// }
builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build()));
builder.setNetworkConfig(coapConfig);
return builder.build();
}
};
LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
builder.setLocalAddress("0.0.0.0", 11000);
builder.setObjects(initializer.createAll());
@ -246,6 +212,11 @@ public class LwM2MTestClient {
public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) {
log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId());
}
@Override
public void onUnexpectedError(Throwable unexpectedError) {
}
};
this.client.addObserver(observer);

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

@ -97,7 +97,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
}
@Override
public WriteResponse write(ServerIdentity identity, int resourceid, LwM2mResource value) {
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceid, LwM2mResource value) {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
switch (resourceid) {
@ -112,7 +112,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
fireResourcesChange(resourceid);
return WriteResponse.success();
default:
return super.write(identity, resourceid, value);
return super.write(identity, replace, resourceid, value);
}
}

BIN
application/src/test/resources/lwm2m/credentials/clientKeyStore.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/serverKeyStore.jks

Binary file not shown.

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

@ -109,8 +109,8 @@ public class LwM2MTransportBootstrapService {
/** Create credentials */
this.setServerWithCredentials(builder);
/** Set securityStore with new ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
// /** Set securityStore with new ConfigStore */
// builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
/** SecurityStore */
builder.setSecurityStore(lwM2MBootstrapSecurityStore);

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java

@ -69,7 +69,7 @@ public class LwM2MBootstrapConfig {
server0.lifetime = servers.getLifetime();
server0.defaultMinPeriod = servers.getDefaultMinPeriod();
server0.notifIfDisabled = servers.isNotifIfDisabled();
server0.binding = BindingMode.valueOf(servers.getBinding());
server0.binding = BindingMode.parse(servers.getBinding());
configBs.servers.put(0, server0);
/* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */
this.bootstrapServer.setBootstrapServerIs(true);

16
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java

@ -30,7 +30,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
@ -40,7 +40,7 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
import java.util.Iterator;
import java.util.UUID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER;
@ -71,8 +71,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
}
@Override
public List<SecurityInfo> getAllByEndpoint(String endPoint) {
EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
@ -88,7 +88,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} catch (InvalidConfigurationException e) {
log.error("", e);
}
return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo());
return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
}
}
return null;
@ -96,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override
public SecurityInfo getByIdentity(String identity) {
EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
@ -113,7 +113,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
return null;
}
private void setBootstrapConfigScurityInfo(EndpointSecurityInfo store) {
private void setBootstrapConfigScurityInfo(TbLwM2MSecurityInfo store) {
/* BootstrapConfig */
LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
if (lwM2MBootstrapConfig != null) {
@ -150,7 +150,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
}
}
private LwM2MBootstrapConfig getParametersBootstrap(EndpointSecurityInfo store) {
private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) {
try {
LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
if (lwM2MBootstrapConfig != null) {

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

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession;
@ -25,7 +26,7 @@ import org.eclipse.leshan.server.security.SecurityChecker;
import org.eclipse.leshan.server.security.SecurityInfo;
import java.util.Collections;
import java.util.List;
import java.util.Iterator;
@Slf4j
public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager {
@ -50,16 +51,17 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
}
@SuppressWarnings("deprecation")
public BootstrapSession begin(String endpoint, Identity clientIdentity) {
public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
boolean authorized;
if (bsSecurityStore != null) {
List<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())) : bsSecurityStore.getAllByEndpoint(endpoint);
Iterator<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ?
Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())).iterator() : bsSecurityStore.getAllByEndpoint(request.getEndpointName());
log.info("Bootstrap session started securityInfos: [{}]", securityInfos);
authorized = securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos);
authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos);
} else {
authorized = true;
}
DefaultBootstrapSession session = new DefaultBootstrapSession(endpoint, clientIdentity, authorized);
DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized);
log.info("Bootstrap session started : {}", session);
return session;
}

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

@ -22,16 +22,16 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
@ -55,15 +55,15 @@ public class LwM2mCredentialsSecurityInfoValidator {
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
public EndpointSecurityInfo getEndpointSecurityInfo(String endpoint, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
CountDownLatch latch = new CountDownLatch(1);
final EndpointSecurityInfo[] resultSecurityStore = new EndpointSecurityInfo[1];
context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(),
final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1];
context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
String credentialsBody = msg.getCredentials();
resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue);
resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg);
resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile());
latch.countDown();
@ -71,8 +71,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
@Override
public void onError(Throwable e) {
log.trace("[{}] [{}] Failed to process credentials ", endpoint, e);
resultSecurityStore[0] = createSecurityInfo(endpoint, null, null);
log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e);
resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null);
latch.countDown();
}
});
@ -91,8 +91,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue -
* @return SecurityInfo
*/
private EndpointSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
EndpointSecurityInfo result = new EndpointSecurityInfo();
private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
if (credentials != null) {
if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) {
@ -104,6 +104,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
result.setEndpoint(endpoint);
result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode());
} else {
result.setEndpoint(credentials.getClient().getEndpoint());
switch (credentials.getClient().getSecurityConfigClientMode()) {
case NO_SEC:
createClientSecurityInfoNoSec(result);
@ -125,12 +126,12 @@ public class LwM2mCredentialsSecurityInfoValidator {
return result;
}
private void createClientSecurityInfoNoSec(EndpointSecurityInfo result) {
private void createClientSecurityInfoNoSec(TbLwM2MSecurityInfo result) {
result.setSecurityInfo(null);
result.setSecurityMode(NO_SEC);
}
private void createClientSecurityInfoPSK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
private void createClientSecurityInfoPSK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
try {
@ -149,7 +150,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
}
}
private void createClientSecurityInfoRPK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
try {
if (rpkConfig.getKey() != null) {
@ -164,7 +165,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
}
}
private void createClientSecurityInfoX509(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
private void createClientSecurityInfoX509(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
result.setSecurityMode(X509);
}

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

@ -27,6 +27,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
@Component
@RequiredArgsConstructor
@ -34,7 +35,7 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
public class TbLwM2MAuthorizer implements Authorizer {
private final TbLwM2MDtlsSessionStore sessionStorage;
private final TbLwM2mSecurityStore securityStore;
private final TbSecurityStore securityStore;
private final SecurityChecker securityChecker = new SecurityChecker();
private final LwM2mClientContext clientContext;

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

@ -29,22 +29,21 @@ import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import javax.annotation.PostConstruct;
@ -57,8 +56,6 @@ import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@ -66,9 +63,10 @@ import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier {
private final TransportService transportService;
private final TbLwM2MDtlsSessionStore sessionStorage;
private final LwM2MTransportServerConfig config;
private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator;
private final TbEditableSecurityStore securityStore;
@SuppressWarnings("deprecation")
private StaticCertificateVerifier staticCertificateVerifier;
@ -119,48 +117,33 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
String strCert = SslUtil.getCertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1];
CountDownLatch latch = new CountDownLatch(1);
transportService.process(TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(sha3Hash).build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (!StringUtils.isEmpty(msg.getCredentials())) {
deviceCredentialsResponse[0] = msg;
}
latch.countDown();
}
@Override
public void onError(Throwable e) {
log.error(e.getMessage(), e);
latch.countDown();
}
});
if (latch.await(10, TimeUnit.SECONDS)) {
ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
if(!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)){
continue;
}
X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
String certBody = config.getCert();
String endpoint = config.getEndpoint();
if (strCert.equals(certBody)) {
x509CredentialsFound = true;
DeviceProfile deviceProfile = msg.getDeviceProfile();
if (msg.hasDeviceInfo() && deviceProfile != null) {
sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
break;
TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
if (!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)) {
continue;
}
X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
String certBody = config.getCert();
String endpoint = config.getEndpoint();
if (strCert.equals(certBody)) {
x509CredentialsFound = true;
DeviceProfile deviceProfile = msg.getDeviceProfile();
if (msg.hasDeviceInfo() && deviceProfile != null) {
sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
try {
securityStore.put(securityInfo);
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
} else {
log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
break;
}
} else {
log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
}
}
} catch (InterruptedException |
CertificateEncodingException |
} catch (CertificateEncodingException |
CertificateExpiredException |
CertificateNotYetValidException e) {
log.error(e.getMessage(), e);

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/EndpointSecurityInfo.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java

@ -24,7 +24,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
@Data
public class EndpointSecurityInfo {
public class TbLwM2MSecurityInfo {
private ValidateDeviceCredentialsResponse msg;
private SecurityInfo securityInfo;
private SecurityMode securityMode;

375
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java

@ -5,7 +5,7 @@
* 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
* 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,
@ -31,7 +31,6 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.registration.Registration;
@ -58,6 +57,8 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
@ -91,7 +92,6 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWN
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
@ -99,6 +99,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
@ -182,32 +183,38 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
public void onRegistered(Registration registration, Collection<Observation> previousObservations) {
registrationExecutor.submit(() -> {
LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
try {
log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
LwM2mClient lwM2MClient = this.clientContext.registerOrUpdate(registration);
if (lwM2MClient != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
if (sessionInfo != null) {
transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
.setSessionInfo(sessionInfo)
.setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN))
.setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build())
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build())
.build();
transportService.process(msg, null);
this.getInfoFirmwareUpdate(lwM2MClient, null);
this.getInfoSoftwareUpdate(lwM2MClient, null);
this.initLwM2mFromClientValue(registration, lwM2MClient);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
} else {
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
this.clientContext.register(lwM2MClient, registration);
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_INFO + ": Client registered with registration id: " + registration.getId());
SessionInfoProto sessionInfo = lwM2MClient.getSession();
transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
.setSessionInfo(sessionInfo)
.setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN))
.setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build())
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build())
.build();
transportService.process(msg, null);
this.getInfoFirmwareUpdate(lwM2MClient, null);
this.getInfoSoftwareUpdate(lwM2MClient, null);
this.initClientTelemetry(lwM2MClient);
} else {
log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
}
} catch (LwM2MClientStateException stateException) {
if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) {
log.info("[{}] retry registration due to race condition: [{}].", registration.getEndpoint(), stateException.getState());
// Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again.
onRegistered(registration, previousObservations);
} else {
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState());
}
} catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to: " + t.getMessage());
}
});
}
@ -219,25 +226,26 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
public void updatedReg(Registration registration) {
updateRegistrationExecutor.submit(() -> {
LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
try {
LwM2mClient client = clientContext.getOrRegister(registration);
if (client != null && client.getSession() != null) {
SessionInfoProto sessionInfo = client.getSession();
this.reportActivityAndRegister(sessionInfo);
if (registration.getBindingMode().useQueueMode()) {
LwM2mQueuedRequest request;
while ((request = client.getQueuedRequests().poll()) != null) {
request.send();
}
clientContext.updateRegistration(lwM2MClient, registration);
TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession();
this.reportActivityAndRegister(sessionInfo);
if (registration.usesQueueMode()) {
LwM2mQueuedRequest request;
while ((request = lwM2MClient.getQueuedRequests().poll()) != null) {
request.send();
}
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client update Registration", registration.getId());
}
} catch (LwM2MClientStateException stateException) {
if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) {
log.info("[{}] update registration failed because client was already unregistered: [{}].", registration.getEndpoint(), stateException.getState());
} else {
log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + ": Client update Registration", registration.getId());
log.info("[{}] update registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage());
}
} catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()), registration.getId());
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()));
}
});
}
@ -248,34 +256,32 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
public void unReg(Registration registration, Collection<Observation> observations) {
unRegistrationExecutor.submit(() -> {
LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint());
try {
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
this.closeClientSession(registration);
this.sendLogsToThingsboard(client, LOG_LW2M_INFO + ": Client unRegistration");
clientContext.unregister(client, registration);
SessionInfoProto sessionInfo = client.getSession();
if (sessionInfo != null) {
transportService.deregisterSession(sessionInfo);
sessionStore.remove(registration.getEndpoint());
this.doCloseSession(sessionInfo);
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
} catch (LwM2MClientStateException stateException) {
log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage());
} catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId());
this.sendLogsToThingsboard(client, LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()));
}
});
}
private void closeClientSession(Registration registration) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
if (sessionInfo != null) {
transportService.deregisterSession(sessionInfo);
sessionStore.remove(registration.getEndpoint());
this.doCloseSession(sessionInfo);
clientContext.removeClientByRegistrationId(registration.getId());
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
}
@Override
public void onSleepingDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId());
this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is sleeping!");
//TODO: associate endpointId with device information.
}
@ -285,8 +291,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override
public void setCancelObservationsAll(Registration registration) {
if (registration != null) {
this.lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL,
null, null, this.config.getTimeout(), null);
LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint());
if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) {
this.lwM2mTransportRequest.sendAllRequest(client, null, OBSERVE_CANCEL_ALL,
null, null, this.config.getTimeout(), null);
}
}
}
@ -300,7 +309,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override
public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
if (response.getContent() != null) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider());
if (objectModelVersion != null) {
if (response.getContent() instanceof LwM2mObject) {
@ -332,7 +341,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO,
READ, pathIdVer, value);
this.sendLogsToThingsboard(msg, registration.getId());
this.sendLogsToThingsboard(lwM2MClient, msg);
rpcRequest.setValueMsg(String.format("%s", value));
this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
}
@ -350,9 +359,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo);
LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
log.warn ("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList());
log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList());
msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey();
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
@ -377,13 +386,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
LOG_LW2M_ERROR, pathIdVer, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
this.sendLogsToThingsboard(lwM2MClient, logMsg);
}
} else if (!isFwSwWords(pathName)) {
log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
LOG_LW2M_ERROR, pathName, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
this.sendLogsToThingsboard(lwM2MClient, logMsg);
}
});
@ -396,9 +405,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
});
log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
}
else if (lwM2MClient == null) {
log.error ("OnAttributeUpdate, lwM2MClient is null");
} else if (lwM2MClient == null) {
log.error("OnAttributeUpdate, lwM2MClient is null");
}
}
@ -408,12 +416,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
@Override
public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
Set<LwM2mClient> clients = clientContext.getLwM2mClients()
.stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toSet());
List<LwM2mClient> clients = clientContext.getLwM2mClients()
.stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList());
clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile));
Set<String> registrationIds = clients.stream().map(LwM2mClient::getRegistration).map(Registration::getId).collect(Collectors.toSet());
if (registrationIds.size() > 0) {
this.onDeviceProfileUpdate(registrationIds, deviceProfile);
if (clients.size() > 0) {
this.onDeviceProfileUpdate(clients, deviceProfile);
}
}
@ -446,7 +453,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) {
// #1
this.checkRpcRequestTimeout();
log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null";
LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName());
UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB());
@ -454,13 +461,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime());
Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
try {
Registration registration = clientContext.getClient(sessionInfo).getRegistration();
LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo);
Registration registration = client.getRegistration();
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this);
if (lwm2mClientRpcRequest.getErrorMsg() != null) {
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
} else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(),
lwM2mTransportRequest.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(),
null,
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.config.getTimeout(), lwm2mClientRpcRequest);
@ -506,7 +514,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override
public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) {
log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
transportService.process(sessionInfo, toDeviceResponse, null);
}
@ -514,17 +522,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
log.info("[{}] toServerRpcResponse", toServerResponse);
}
/**
* Trigger Server path = "/1/0/8"
* <p>
* Trigger bootStrap path = "/1/0/9" - have to implemented on client
*/
@Override
public void doTrigger(Registration registration, String path) {
lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE,
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
}
/**
* Deregister session in transport
*
@ -558,7 +555,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override
public void onAwakeDev(Registration registration) {
log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is awake!");
//TODO: associate endpointId with device information.
}
@ -567,13 +564,17 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param registrationId - Id of Registration LwM2M Client
*/
@Override
public void sendLogsToThingsboard(String logMsg, String registrationId) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registrationId);
if (logMsg != null && sessionInfo != null) {
public void sendLogsToThingsboard(String registrationId, String logMsg) {
sendLogsToThingsboard(clientContext.getClientByRegistrationId(registrationId), logMsg);
}
@Override
public void sendLogsToThingsboard(LwM2mClient client, String logMsg) {
if (logMsg != null && client != null && client.getSession() != null) {
if (logMsg.length() > 1024) {
logMsg = logMsg.substring(0, 1024);
}
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), client.getSession());
}
}
@ -586,24 +587,23 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* - Read Request to the client after registration to read all resource values for all objects
* - then Observe Request to the client marked as observe from the profile configuration.
*
* @param registration - Registration LwM2M Client
* @param lwM2MClient - object with All parameters off client
* @param lwM2MClient - object with All parameters off client
*/
private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration);
private void initClientTelemetry(LwM2mClient lwM2MClient) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId());
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(lwM2MClient);
if (clientObjects != null && clientObjects.size() > 0) {
if (LWM2M_STRATEGY_2 == LwM2mTransportUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
// #2
lwM2MClient.getPendingReadRequests().addAll(clientObjects);
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ,
null, this.config.getTimeout(), null));
}
// #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, OBSERVE, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, WRITE_ATTRIBUTES, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, DISCOVER, clientObjects);
this.initReadAttrTelemetryObserveToClient(lwM2MClient, READ, clientObjects);
this.initReadAttrTelemetryObserveToClient(lwM2MClient, OBSERVE, clientObjects);
this.initReadAttrTelemetryObserveToClient(lwM2MClient, WRITE_ATTRIBUTES, clientObjects);
this.initReadAttrTelemetryObserveToClient(lwM2MClient, DISCOVER, clientObjects);
}
}
@ -645,7 +645,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param path - resource
*/
private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) {
/** version != null
* set setClient_fw_info... = value
@ -740,15 +740,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
}
/**
* Start observe/read: Attr/Telemetry
* #1 - Analyze: path in resource profile == client resource
*
* @param registration -
*/
private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient,
LwM2mTypeOper typeOper, Set<String> clientObjects) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, Set<String> clientObjects) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId());
Set<String> result = null;
ConcurrentHashMap<String, Object> params = null;
if (READ.equals(typeOper)) {
@ -768,28 +761,42 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
result = params.keySet();
}
if (result != null && !result.isEmpty()) {
// #1
Set<String> pathSend = result.stream().filter(target -> {
return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]);
}
sendRequestsToClient(lwM2MClient, typeOper, clientObjects, result, params);
}
private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set<String> supportedObjectIds, Set<String> desiredObjectIds, ConcurrentHashMap<String, Object> params) {
if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) {
Set<String> targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target)
).collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
lwM2MClient.getPendingReadRequests().addAll(pathSend);
ConcurrentHashMap<String, Object> finalParams = params;
pathSend.forEach(target -> {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
finalParams != null ? finalParams.get(target) : null, this.config.getTimeout(), null);
if (!targetObjectIds.isEmpty()) {
//TODO: remove this side effect?
lwM2MClient.getPendingReadRequests().addAll(targetObjectIds);
targetObjectIds.forEach(target -> {
Object additionalParams = params != null ? params.get(target) : null;
lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null);
});
if (OBSERVE.equals(typeOper)) {
if (OBSERVE.equals(operationType)) {
lwM2MClient.initReadValue(this, null);
}
}
}
}
private boolean isSupportedTargetId(Set<String> supportedIds, String targetId) {
String[] targetIdParts = targetId.split(LWM2M_SEPARATOR_PATH);
if (targetIdParts.length <= 1) {
return false;
}
String targetIdSearch = targetIdParts[0];
for (int i = 1; i < targetIdParts.length; i++) {
targetIdSearch += "/" + targetIdParts[i];
if (supportedIds.contains(targetIdSearch)) {
return true;
}
}
return false;
}
private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) {
ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() {
@ -798,7 +805,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) {
deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singleton(lwM2MClient.getRegistration().getId()), deviceProfile));
deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), deviceProfile));
lwM2MClient.onDeviceUpdate(device, deviceProfileOpt);
}
@ -843,7 +850,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) {
LwM2mClient lwM2MClient = this.clientContext.getClientByRegistrationId(registration.getId());
LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile();
if (names != null && names.has(pathIdVer)) {
String resourceName = names.get(pathIdVer).getAsString();
@ -858,7 +865,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
valueKvProto = new JsonObject();
Object finalvalueKvProto = valueKvProto;
Gson gson = new GsonBuilder().create();
resourceValue.getValues().forEach((k, v) -> {
resourceValue.getInstances().forEach((k, v) -> {
Object val = this.converter.convertValue(v, currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
JsonElement element = gson.toJsonTree(val, val.getClass());
@ -892,9 +899,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
}
else {
} else {
return null;
}
}
@ -955,10 +960,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* #6.1 - update WriteAttribute
* #6.2 - del WriteAttribute
*
* @param registrationIds -
* @param deviceProfile -
* @param clients -
* @param deviceProfile -
*/
private void onDeviceProfileUpdate(Set<String> registrationIds, DeviceProfile deviceProfile) {
private void onDeviceProfileUpdate(List<LwM2mClient> clients, DeviceProfile deviceProfile) {
LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
if (clientContext.profileUpdate(deviceProfile) != null) {
// #1
@ -1009,15 +1014,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// #3.4, #6
if (!attributeLwm2mOld.equals(attributeLwm2mNew)) {
this.getAnalyzerAttributeLwm2m(registrationIds, attributeLwm2mOld, attributeLwm2mNew);
this.getAnalyzerAttributeLwm2m(clients, attributeLwm2mOld, attributeLwm2mNew);
}
// #4.1 add
if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
// update value in Resources
registrationIds.forEach(registrationId -> {
Registration registration = clientContext.getRegistration(registrationId);
this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
clients.forEach(client -> {
this.readObserveFromProfile(client, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
});
}
// #4.2 del
@ -1041,15 +1045,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// does not include oldObserve
ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd());
// send Request observe to Client
registrationIds.forEach(registrationId -> {
Registration registration = clientContext.getRegistration(registrationId);
clients.forEach(client -> {
Registration registration = client.getRegistration();
if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
this.readObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
}
// 5.3 del
// send Request cancel observe to Client
if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel());
this.cancelObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersDel());
}
});
}
@ -1086,19 +1090,18 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* Update Resource value after change RezAttrTelemetry in config Profile
* send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
*
* @param registration - Registration LwM2M Client
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
*/
private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) {
private void readObserveFromProfile(LwM2mClient client, Set<String> targets, LwM2mTypeOper typeOper) {
targets.forEach(target -> {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
if (pathIds.isResource()) {
if (READ.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
lwM2mTransportRequest.sendAllRequest(client, target, typeOper,
null, this.config.getTimeout(), null);
} else if (OBSERVE.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
null, null, this.config.getTimeout(), null);
lwM2mTransportRequest.sendAllRequest(client, target, typeOper,
null, this.config.getTimeout(), null);
}
}
});
@ -1124,7 +1127,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param attributeLwm2mNew -
* @return
*/
private void getAnalyzerAttributeLwm2m(Set<String> registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) {
private void getAnalyzerAttributeLwm2m(List<LwM2mClient> clients, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) {
ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() {
@ -1146,23 +1149,22 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// #6
// #6.2
if (analyzerParameters.getPathPostParametersAdd().size() > 0) {
registrationIds.forEach(registrationId -> {
Registration registration = this.clientContext.getRegistration(registrationId);
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration);
clients.forEach(client -> {
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client);
Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES,
finalParams.get(target), this.config.getTimeout(), null));
}
});
}
// #6.2
if (analyzerParameters.getPathPostParametersDel().size() > 0) {
registrationIds.forEach(registrationId -> {
Registration registration = this.clientContext.getRegistration(registrationId);
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration);
clients.forEach(client -> {
Registration registration = client.getRegistration();
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client);
Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
@ -1170,8 +1172,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target);
params.clear();
params.put(OBJECT_VERSION, "");
lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
params, this.config.getTimeout(), null);
lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, params, this.config.getTimeout(), null);
});
}
});
@ -1179,12 +1180,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set<String> paramAnallyzer) {
paramAnallyzer.forEach(pathIdVer -> {
if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null,
null, this.config.getTimeout(), null);
if (this.getResourceValueFromLwM2MClient(lwM2mClient, pathIdVer) != null) {
lwM2mTransportRequest.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null);
}
}
);
@ -1192,14 +1191,12 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) {
if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE,
ContentFormat.TLV.getName(), valueNew,
this.config.getTimeout(), null);
lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null);
} else {
log.error("Failed update resource [{}] [{}]", path, valueNew);
String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
LOG_LW2M_ERROR, path, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
this.sendLogsToThingsboard(lwM2MClient, logMsg);
log.info("Failed update resource [{}] [{}]", path, valueNew);
}
}
@ -1221,7 +1218,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo);
LwM2mClient lwM2mClient = clientContext.getClientBySessionInfo(sessionInfo);
return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
.filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey)
.orElse(null);
@ -1256,7 +1253,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param sessionInfo
*/
public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo);
LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
if (lwM2MClient != null) {
log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos);
tsKvProtos.forEach(tsKvProto -> {
@ -1275,8 +1272,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
});
}
else {
} else {
log.error("UpdateAttributeFromThingsboard, lwM2MClient is null");
}
}
@ -1285,14 +1281,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param lwM2MClient -
* @return SessionInfoProto -
*/
private SessionInfoProto getSessionInfoOrCloseSession(LwM2mClient lwM2MClient) {
if (lwM2MClient != null) {
SessionInfoProto sessionInfoProto = lwM2MClient.getSession();
if (sessionInfoProto == null) {
log.info("[{}] [{}]", lwM2MClient.getEndpoint(), CLIENT_NOT_AUTHORIZED);
this.closeClientSession(lwM2MClient.getRegistration());
}
return sessionInfoProto;
private SessionInfoProto getSessionInfo(LwM2mClient lwM2MClient) {
if (lwM2MClient != null && lwM2MClient.getSession() != null) {
return lwM2MClient.getSession();
}
return null;
}
@ -1302,15 +1293,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @return - sessionInfo after access connect client
*/
public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) {
return getSessionInfoOrCloseSession(clientContext.getOrRegister(registration));
}
/**
* @param registrationId -
* @return -
*/
private SessionInfoProto getSessionInfoOrCloseSession(String registrationId) {
return getSessionInfoOrCloseSession(clientContext.getClientByRegistrationId(registrationId));
return getSessionInfo(clientContext.getClientByEndpoint(registration.getEndpoint()));
}
/**
@ -1340,7 +1323,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param lwM2MClient - LwM2M Client
*/
public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) {
//#1.1
ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
@ -1359,7 +1342,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler handler = this;
this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()),
@ -1368,16 +1351,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
&& response.getType().equals(OtaPackageType.FIRMWARE.name())) {
log.warn ("7) firmware start with ver: [{}]", response.getVersion());
log.warn("7) firmware start with ver: [{}]", response.getVersion());
lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest);
lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion());
lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
if (rpcRequest == null) {
lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
}
else {
lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
} else {
lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
}
} else {
log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
@ -1395,7 +1377,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler handler = this;
transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()),
@ -1411,9 +1393,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
if (rpcRequest == null) {
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
}
else {
lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
} else {
lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
}
} else {
log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());

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

@ -36,6 +36,8 @@ import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct;
@ -83,7 +85,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private final LwM2mTransportServerHelper helper;
private final LwM2mTransportMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore;
private final EditableSecurityStore securityStore;
private final TbSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext;
private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
private final TbLwM2MAuthorizer authorizer;

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

@ -87,7 +87,7 @@ public class LwM2mServerListener {
@Override
public void cancelled(Observation observation) {
String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath());
service.sendLogsToThingsboard(msg, observation.getRegistrationId());
service.sendLogsToThingsboard(observation.getRegistrationId(), msg);
log.warn(msg);
}
@ -109,7 +109,7 @@ public class LwM2mServerListener {
String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
observation.getPath());
log.warn(msg);
service.sendLogsToThingsboard(msg, registration.getId());
service.sendLogsToThingsboard(registration.getId(), msg);
}
};
}

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

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
import java.util.Collection;
@ -57,13 +58,13 @@ public interface LwM2mTransportMsgHandler {
void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse);
void doTrigger(Registration registration, String path);
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
void onAwakeDev(Registration registration);
void sendLogsToThingsboard(String msg, String registrationId);
void sendLogsToThingsboard(LwM2mClient client, String msg);
void sendLogsToThingsboard(String registrationId, String msg);
LwM2MTransportServerConfig getConfig();
}

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

@ -32,10 +32,10 @@ import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.DeleteRequest;
import org.eclipse.leshan.core.request.DiscoverRequest;
import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.ExecuteRequest;
import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.request.exception.ClientSleepingException;
@ -116,32 +116,30 @@ public class LwM2mTransportRequest {
new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL)));
}
/**
* Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes
*
* @param registration -
* @param targetIdVer -
* @param typeOper -
* @param contentFormatName -
*/
public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
sendAllRequest(lwM2MClient, targetIdVer, typeOper, lwM2MClient.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest);
}
public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper,
ContentFormat contentFormat, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
Registration registration = lwM2MClient.getRegistration();
try {
String target = convertPathFromIdVerToObjectId(targetIdVer);
ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration);
if(contentFormat == null){
contentFormat = ContentFormat.DEFAULT;
}
LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null;
if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
SimpleDownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
targetIdVer, resultIds, params, lwm2mClientRpcRequest);
if (request != null) {
try {
this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest);
} catch (ClientSleepingException e) {
DownlinkRequest finalRequest = request;
SimpleDownlinkRequest finalRequest = request;
long finalTimeoutInMs = timeoutInMs;
Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest;
lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
@ -185,7 +183,7 @@ public class LwM2mTransportRequest {
}
String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
typeOper.name(), paths);
this.handler.sendLogsToThingsboard(msg, registration.getId());
this.handler.sendLogsToThingsboard(lwM2MClient, msg);
if (lwm2mClientRpcRequest != null) {
String valueMsg = String.format("Paths - %s", paths);
this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
@ -204,7 +202,7 @@ public class LwM2mTransportRequest {
observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), observeCancelCnt);
}
this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest);
this.afterObserveCancel(lwM2MClient, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest);
break;
// lwm2mClientRpcRequest != null
case FW_UPDATE:
@ -215,7 +213,7 @@ public class LwM2mTransportRequest {
} catch (Exception e) {
String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
typeOper.name(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId());
handler.sendLogsToThingsboard(lwM2MClient, msg);
if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@ -223,10 +221,10 @@ public class LwM2mTransportRequest {
}
}
private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
ContentFormat contentFormat, String target, String targetIdVer,
LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
DownlinkRequest request = null;
SimpleDownlinkRequest request = null;
switch (typeOper) {
case READ:
request = new ReadRequest(contentFormat, target);
@ -273,7 +271,7 @@ public class LwM2mTransportRequest {
contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
registration, rpcRequest);
lwM2MClient, rpcRequest);
}
break;
case WRITE_UPDATE:
@ -329,7 +327,7 @@ public class LwM2mTransportRequest {
*/
@SuppressWarnings({"error sendRequest"})
private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request,
private void sendRequest(Registration registration, LwM2mClient lwM2MClient, SimpleDownlinkRequest request,
long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
@ -337,11 +335,11 @@ public class LwM2mTransportRequest {
lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
}
if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
this.handleResponse(lwM2MClient, request.getPath().toString(), response, request, rpcRequest);
} else {
String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
handler.sendLogsToThingsboard(msg, registration.getId());
handler.sendLogsToThingsboard(lwM2MClient, msg);
log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
if (!lwM2MClient.isInit()) {
@ -388,7 +386,7 @@ public class LwM2mTransportRequest {
}
String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId());
handler.sendLogsToThingsboard(lwM2MClient, msg);
log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
@ -398,7 +396,7 @@ public class LwM2mTransportRequest {
private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
Integer resourceId, Object value, ResourceModel.Type type,
Registration registration, Lwm2mClientRpcRequest rpcRequest) {
LwM2mClient client, Lwm2mClientRpcRequest rpcRequest) {
try {
if (type != null) {
switch (type) {
@ -433,7 +431,7 @@ public class LwM2mTransportRequest {
String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
patn, type, value, e.toString());
handler.sendLogsToThingsboard(msg, registration.getId());
handler.sendLogsToThingsboard(client, msg);
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
if (rpcRequest != null) {
String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@ -443,13 +441,13 @@ public class LwM2mTransportRequest {
}
}
private void handleResponse(Registration registration, final String path, LwM2mResponse response,
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
private void handleResponse(LwM2mClient lwM2mClient, final String path, LwM2mResponse response,
SimpleDownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
responseRequestExecutor.submit(() -> {
try {
this.sendResponse(registration, path, response, request, rpcRequest);
this.sendResponse(lwM2mClient, path, response, request, rpcRequest);
} catch (Exception e) {
log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e);
log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", lwM2mClient.getRegistration().getEndpoint(), path, e);
}
});
}
@ -461,8 +459,9 @@ public class LwM2mTransportRequest {
* @param path -
* @param response -
*/
private void sendResponse(Registration registration, String path, LwM2mResponse response,
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response,
SimpleDownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
Registration registration = lwM2mClient.getRegistration();
String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
String msgLog = "";
if (response instanceof ReadResponse) {
@ -477,7 +476,7 @@ public class LwM2mTransportRequest {
String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks());
msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
handler.sendLogsToThingsboard(msgLog, registration.getId());
handler.sendLogsToThingsboard(lwM2mClient, msgLog);
log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
@ -486,7 +485,7 @@ public class LwM2mTransportRequest {
msgLog = String.format("%s: type operation: %s path: %s",
LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString());
log.warn("9) [{}] ", msgLog);
handler.sendLogsToThingsboard(msgLog, registration.getId());
handler.sendLogsToThingsboard(lwM2mClient, msgLog);
if (rpcRequest != null) {
msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg());
rpcRequest.setInfoMsg(msgLog);
@ -496,7 +495,7 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteAttributesResponse) {
msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString());
handler.sendLogsToThingsboard(msgLog, registration.getId());
handler.sendLogsToThingsboard(lwM2mClient, msgLog);
log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response);
if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE);
@ -504,13 +503,14 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteResponse) {
msgLog = String.format("Type operation: Write path: %s", pathIdVer);
log.warn("10) [{}] response: [{}]", msgLog, response);
this.infoWriteResponse(registration, response, request, rpcRequest);
this.infoWriteResponse(lwM2mClient, response, request, rpcRequest);
handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
}
}
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
try {
Registration registration = lwM2mClient.getRegistration();
LwM2mNode node = ((WriteRequest) request).getNode();
String msg = null;
Object value;
@ -545,7 +545,7 @@ public class LwM2mTransportRequest {
}
}
if (msg != null) {
handler.sendLogsToThingsboard(msg, registration.getId());
handler.sendLogsToThingsboard(lwM2mClient, msg);
if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
this.afterWriteSuccessFwSwUpdate(registration, request);
if (rpcRequest != null) {
@ -566,7 +566,7 @@ public class LwM2mTransportRequest {
* fw_state/sw_state = DOWNLOADED
* send operation Execute
*/
private void afterWriteSuccessFwSwUpdate(Registration registration, DownlinkRequest request) {
private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
@ -581,7 +581,7 @@ public class LwM2mTransportRequest {
/**
* After finish operation FwSwUpdate Write (error): fw_state = FAILED
*/
private void afterWriteFwSWUpdateError(Registration registration, DownlinkRequest request, String msgError) {
private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
@ -593,7 +593,7 @@ public class LwM2mTransportRequest {
}
}
private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) {
private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
@ -603,8 +603,8 @@ public class LwM2mTransportRequest {
}
}
private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) {
handler.sendLogsToThingsboard(observeCancelMsg, registration.getId());
private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) {
handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg);
log.warn("[{}]", observeCancelMsg);
if (rpcRequest != null) {
rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));

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

@ -137,7 +137,7 @@ public class LwM2mTransportServerHelper {
public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
try {
DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0);
return ddfFileParser.parse(new ByteArrayInputStream(xmlByte), streamName).get(0);
} catch (IOException | InvalidDDFFileException e) {
log.error("Could not parse the XML file [{}]", streamName, e);
return null;

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

@ -36,7 +36,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.registration.Registration;
@ -839,7 +839,7 @@ public class LwM2mTransportUtil {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st};
*/
public static DownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) {
public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
}

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientState.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.client;
public enum LwM2MClientState {
CREATED, REGISTERED, UNREGISTERED
}

31
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientStateException.java

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.client;
import lombok.Getter;
public class LwM2MClientStateException extends Exception {
private static final long serialVersionUID = 3307690997951364046L;
@Getter
private final LwM2MClientState state;
public LwM2MClientStateException(LwM2MClientState state, String message) {
super(message);
this.state = state;
}
}

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

@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo;
@ -49,6 +50,8 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
@ -62,12 +65,28 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.g
@Slf4j
public class LwM2mClient implements Cloneable {
private final String nodeId;
@Getter
private final String endpoint;
private final Lock lock;
@Getter
@Setter
private LwM2MClientState state;
@Getter
private final Map<String, ResourceValue> resources;
@Getter
private final Map<String, TsKvProto> delayedRequests;
@Getter
private final List<String> pendingReadRequests;
@Getter
private final Queue<LwM2mQueuedRequest> queuedRequests;
@Getter
private String deviceName;
@Getter
private String deviceProfileName;
@Getter
private String endpoint;
@Getter
private String identity;
@Getter
@ -92,15 +111,6 @@ public class LwM2mClient implements Cloneable {
private ValidateDeviceCredentialsResponse credentials;
@Getter
private final Map<String, ResourceValue> resources;
@Getter
private final Map<String, TsKvProto> delayedRequests;
@Getter
@Setter
private final List<String> pendingReadRequests;
@Getter
private final Queue<LwM2mQueuedRequest> queuedRequests;
@Getter
private boolean init;
@ -108,18 +118,23 @@ public class LwM2mClient implements Cloneable {
return super.clone();
}
public LwM2mClient(String nodeId, String endpoint, String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) {
public LwM2mClient(String nodeId, String endpoint) {
this.nodeId = nodeId;
this.endpoint = endpoint;
this.identity = identity;
this.securityInfo = securityInfo;
this.credentials = credentials;
this.lock = new ReentrantLock();
this.delayedRequests = new ConcurrentHashMap<>();
this.pendingReadRequests = new CopyOnWriteArrayList<>();
this.resources = new ConcurrentHashMap<>();
this.profileId = profileId;
this.init = false;
this.queuedRequests = new ConcurrentLinkedQueue<>();
this.state = LwM2MClientState.CREATED;
}
public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) {
this.identity = identity;
this.securityInfo = securityInfo;
this.credentials = credentials;
this.profileId = profileId;
this.init = false;
this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE);
this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE);
if (this.credentials != null && this.credentials.hasDeviceInfo()) {
@ -131,6 +146,14 @@ public class LwM2mClient implements Cloneable {
}
}
public void lock() {
lock.lock();
}
public void unlock() {
lock.unlock();
}
public void onDeviceUpdate(Device device, Optional<DeviceProfile> deviceProfileOpt) {
SessionInfoProto.Builder builder = SessionInfoProto.newBuilder().mergeFrom(session);
this.deviceId = device.getUuidId();
@ -193,9 +216,7 @@ public class LwM2mClient implements Cloneable {
public Object getResourceValue(String pathRezIdVer, String pathRezId) {
String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
if (this.resources.get(pathRez) != null) {
return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
this.resources.get(pathRez).getLwM2mResource().getValues() :
this.resources.get(pathRez).getLwM2mResource().getValue();
return this.resources.get(pathRez).getLwM2mResource().getValue();
}
return null;
}
@ -366,5 +387,14 @@ public class LwM2mClient implements Cloneable {
}
}
public ContentFormat getDefaultContentFormat() {
if (registration == null) {
return ContentFormat.DEFAULT;
} else if (registration.getLwM2mVersion().equals("1.0")) {
return ContentFormat.TLV;
} else {
return ContentFormat.TEXT;
}
}
}

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

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.server.client;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -27,21 +28,17 @@ import java.util.UUID;
public interface LwM2mClientContext {
void removeClientByRegistrationId(String registrationId);
LwM2mClient getClientByEndpoint(String endpoint);
LwM2mClient getClientByRegistrationId(String registrationId);
LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo);
LwM2mClient getClientByEndpoint(String endpoint);
LwM2mClient getOrRegister(Registration registration);
LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo);
LwM2mClient registerOrUpdate(Registration registration);
void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException;
LwM2mClient fetchClientByEndpoint(String endpoint);
void updateRegistration(LwM2mClient client, Registration registration) throws LwM2MClientStateException;
Registration getRegistration(String registrationId);
void unregister(LwM2mClient client, Registration registration) throws LwM2MClientStateException;
Collection<LwM2mClient> getLwM2mClients();
@ -55,9 +52,11 @@ public interface LwM2mClientContext {
LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile);
Set<String> getSupportedIdVerInClient(Registration registration);
Set<String> getSupportedIdVerInClient(LwM2mClient registration);
LwM2mClient getClientByDeviceId(UUID deviceId);
void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
}

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

@ -19,16 +19,15 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import java.util.Arrays;
import java.util.Collection;
@ -48,46 +47,107 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c
public class LwM2mClientContextImpl implements LwM2mClientContext {
private final LwM2mTransportContext context;
private final TbEditableSecurityStore securityStore;
private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>();
private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>();
private Map<UUID, LwM2mClientProfile> profiles = new ConcurrentHashMap<>();
private final LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
private final EditableSecurityStore securityStore;
@Override
public LwM2mClient getClientByEndpoint(String endpoint) {
return lwM2mClientsByEndpoint.get(endpoint);
return lwM2mClientsByEndpoint.computeIfAbsent(endpoint, ep -> new LwM2mClient(context.getNodeId(), ep));
}
@Override
public LwM2mClient getClientByRegistrationId(String registrationId) {
return lwM2mClientsByRegistrationId.get(registrationId);
public void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
lwM2MClient.lock();
try {
if (LwM2MClientState.UNREGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint());
if (securityInfo.getSecurityMode() != null) {
if (securityInfo.getDeviceProfile() != null) {
UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile()) != null ? securityInfo.getDeviceProfile().getUuidId() : null;
if (securityInfo.getSecurityInfo() != null) {
lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
lwM2MClient.init(null, null, securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint()));
}
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint()));
}
} else {
throw new RuntimeException(String.format("Registration failed: FORBIDDEN, endpointId: %s", lwM2MClient.getEndpoint()));
}
lwM2MClient.setRegistration(registration);
this.lwM2mClientsByRegistrationId.put(registration.getId(), lwM2MClient);
lwM2MClient.setState(LwM2MClientState.REGISTERED);
} finally {
lwM2MClient.unlock();
}
}
@Override
public LwM2mClient getOrRegister(Registration registration) {
if (registration == null) {
return null;
public void updateRegistration(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
lwM2MClient.lock();
try {
if (!LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
Registration currentRegistration = lwM2MClient.getRegistration();
if (currentRegistration.getId().equals(registration.getId())) {
lwM2MClient.setRegistration(registration);
} else {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client has different registration.");
}
} finally {
lwM2MClient.unlock();
}
LwM2mClient client = lwM2mClientsByRegistrationId.get(registration.getId());
if (client == null) {
client = lwM2mClientsByEndpoint.get(registration.getEndpoint());
if (client == null) {
client = registerOrUpdate(registration);
}
@Override
public void unregister(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
lwM2MClient.lock();
try {
if (!LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
lwM2mClientsByRegistrationId.remove(registration.getId());
Registration currentRegistration = lwM2MClient.getRegistration();
if (currentRegistration.getId().equals(registration.getId())) {
lwM2MClient.setState(LwM2MClientState.UNREGISTERED);
lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
this.securityStore.remove(lwM2MClient.getEndpoint());
this.lwM2mClientsByRegistrationId.remove(registration.getId());
UUID profileId = lwM2MClient.getProfileId();
if (profileId != null) {
Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
if (otherClients.isEmpty()) {
profiles.remove(profileId);
}
}
} else {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client has different registration.");
}
} finally {
lwM2MClient.unlock();
}
return client;
}
@Override
public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c ->
public LwM2mClient getClientByRegistrationId(String registrationId) {
return lwM2mClientsByRegistrationId.get(registrationId);
}
@Override
public LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c ->
(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()))
.equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB())))
).findAny().get();
).findAny().orElse(null);
if (lwM2mClient == null) {
log.warn("Device TimeOut? lwM2mClient is null.");
log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size());
@ -96,60 +156,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
return lwM2mClient;
}
@Override
public LwM2mClient registerOrUpdate(Registration registration) {
LwM2mClient lwM2MClient = lwM2mClientsByEndpoint.get(registration.getEndpoint());
if (lwM2MClient == null) {
lwM2MClient = this.fetchClientByEndpoint(registration.getEndpoint());
}
lwM2MClient.setRegistration(registration);
// TODO: this remove is probably redundant. We should remove it.
// this.lwM2mClientsByEndpoint.remove(registration.getEndpoint());
this.lwM2mClientsByRegistrationId.put(registration.getId(), lwM2MClient);
return lwM2MClient;
}
public Registration getRegistration(String registrationId) {
return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration();
}
@Override
public LwM2mClient fetchClientByEndpoint(String endpoint) {
EndpointSecurityInfo securityInfo = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endpoint, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
if (securityInfo.getSecurityMode() != null) {
if (securityInfo.getDeviceProfile() != null) {
UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile())!= null ?
securityInfo.getDeviceProfile().getUuidId() : null;
// TODO: for tests bug.
if (profileUuid== null) {
log.trace("input parameters toClientProfile if the result is null: [{}]", securityInfo.getDeviceProfile());
}
LwM2mClient client;
if (securityInfo.getSecurityInfo() != null) {
client = new LwM2mClient(context.getNodeId(), securityInfo.getSecurityInfo().getEndpoint(),
securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(),
securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
client = new LwM2mClient(context.getNodeId(), endpoint,
null, null,
securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", endpoint));
}
lwM2mClientsByEndpoint.put(client.getEndpoint(), client);
return client;
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", endpoint));
}
} else {
throw new RuntimeException(String.format("Registration failed: FORBIDDEN, endpointId: %s", endpoint));
}
}
@Override
public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) {
LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
lwM2mClientsByEndpoint.put(registration.getEndpoint(), client);
LwM2mClient client = getClientByEndpoint(registration.getEndpoint());
client.init(null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
lwM2mClientsByRegistrationId.put(registration.getId(), client);
profileUpdate(credentials.getDeviceProfile());
}
@ -171,7 +185,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override
public LwM2mClientProfile getProfile(Registration registration) {
return this.getProfiles().get(getOrRegister(registration).getProfileId());
return this.getProfiles().get(getClientByEndpoint(registration.getEndpoint()).getProfileId());
}
@Override
@ -186,25 +200,18 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
if (lwM2MClientProfile != null) {
profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile);
return lwM2MClientProfile;
}
else {
} else {
return null;
}
}
/**
* if isVer - ok or default ver=DEFAULT_LWM2M_VERSION
*
* @param registration -
* @return - all objectIdVer in client
*/
@Override
public Set<String> getSupportedIdVerInClient(Registration registration) {
public Set<String> getSupportedIdVerInClient(LwM2mClient client) {
Set<String> clientObjects = ConcurrentHashMap.newKeySet();
Arrays.stream(registration.getObjectLinks()).forEach(url -> {
LwM2mPath pathIds = new LwM2mPath(url.getUrl());
Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> {
LwM2mPath pathIds = new LwM2mPath(link.getUrl());
if (!pathIds.isRoot()) {
clientObjects.add(convertPathFromObjectIdToIdVer(url.getUrl(), registration));
clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration()));
}
});
return (clientObjects.size() > 0) ? clientObjects : null;
@ -215,20 +222,4 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null);
}
@Override
public void removeClientByRegistrationId(String registrationId) {
LwM2mClient lwM2MClient = this.lwM2mClientsByRegistrationId.get(registrationId);
if (lwM2MClient != null) {
this.securityStore.remove(lwM2MClient.getEndpoint(), false);
this.lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
this.lwM2mClientsByRegistrationId.remove(registrationId);
UUID profileId = lwM2MClient.getProfileId();
if (profileId != null) {
Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
if (otherClients.isEmpty()) {
profiles.remove(profileId);
}
}
}
}
}

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

@ -167,9 +167,9 @@ public class LwM2mFwSwUpdate {
String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO,
LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID);
handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId());
handler.sendLogsToThingsboard(lwM2MClient, fwMsg);
log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer);
request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE,
firmwareChunk, handler.config.getTimeout(), this.rpcRequest);
}
else {
@ -190,7 +190,7 @@ public class LwM2mFwSwUpdate {
if (LOG_LW2M_ERROR.equals(typeInfo)) {
msg = String.format("%s Error: %s", msg, msgError);
}
handler.sendLogsToThingsboard(msg, lwM2MClient.getRegistration().getId());
handler.sendLogsToThingsboard(lwM2MClient, msg);
}
@ -202,8 +202,7 @@ public class LwM2mFwSwUpdate {
public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
this.setStateUpdate(UPDATING.name());
this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(),
null, 0, this.rpcRequest);
request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest);
}
/**
@ -334,10 +333,10 @@ public class LwM2mFwSwUpdate {
}
private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
request.sendAllRequest(lwM2MClient.getRegistration(),
request.sendAllRequest(lwM2MClient,
convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
null, null, 0, null);
request.sendAllRequest(lwM2MClient.getRegistration(),
request.sendAllRequest(lwM2MClient,
convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
null, null, 0, null);
}
@ -364,8 +363,7 @@ public class LwM2mFwSwUpdate {
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathResultId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.forEach(pathIdVer -> {
request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(),
null, 0, this.rpcRequest);
request.sendAllRequest(this.lwM2MClient, pathIdVer, OBSERVE, null, 0, this.rpcRequest);
});
}

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

@ -273,7 +273,7 @@ public class Lwm2mClientRpcRequest {
}
private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) {
LwM2mClient lwM2mClient = handler.clientContext.getClient(this.sessionInfo);
LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo);
return lwM2mClient != null ?
lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) :
null;

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbEditableSecurityStore.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
public interface TbEditableSecurityStore extends TbSecurityStore {
void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException;
void remove(String endpoint);
}

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

@ -0,0 +1,130 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TbInMemorySecurityStore implements TbEditableSecurityStore {
// lock for the two maps
protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
protected final Lock readLock = readWriteLock.readLock();
protected final Lock writeLock = readWriteLock.writeLock();
// by client end-point
protected Map<String, TbLwM2MSecurityInfo> securityByEp = new HashMap<>();
// by PSK identity
protected Map<String, TbLwM2MSecurityInfo> securityByIdentity = new HashMap<>();
public TbInMemorySecurityStore() {
}
/**
* {@inheritDoc}
*/
@Override
public SecurityInfo getByEndpoint(String endpoint) {
readLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.get(endpoint);
if (securityInfo != null) {
return securityInfo.getSecurityInfo();
} else {
return null;
}
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public SecurityInfo getByIdentity(String identity) {
readLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByIdentity.get(identity);
if (securityInfo != null) {
return securityInfo.getSecurityInfo();
} else {
return null;
}
} finally {
readLock.unlock();
}
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
writeLock.lock();
try {
String identity = null;
if (tbSecurityInfo.getSecurityInfo() != null) {
identity = tbSecurityInfo.getSecurityInfo().getIdentity();
if (identity != null) {
TbLwM2MSecurityInfo infoByIdentity = securityByIdentity.get(identity);
if (infoByIdentity != null && !tbSecurityInfo.getSecurityInfo().getEndpoint().equals(infoByIdentity.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + identity + " is already used");
}
securityByIdentity.put(tbSecurityInfo.getSecurityInfo().getIdentity(), tbSecurityInfo);
}
}
TbLwM2MSecurityInfo previous = securityByEp.put(tbSecurityInfo.getEndpoint(), tbSecurityInfo);
if (previous != null && previous.getSecurityInfo() != null) {
String previousIdentity = previous.getSecurityInfo().getIdentity();
if (previousIdentity != null && !previousIdentity.equals(identity)) {
securityByIdentity.remove(previousIdentity);
}
}
} finally {
writeLock.unlock();
}
}
@Override
public void remove(String endpoint) {
writeLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.remove(endpoint);
if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getIdentity() != null) {
securityByIdentity.remove(securityInfo.getSecurityInfo().getIdentity());
}
} finally {
writeLock.unlock();
}
}
@Override
public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
readLock.lock();
try {
return securityByEp.get(endpoint);
} finally {
readLock.unlock();
}
}
}

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

@ -22,11 +22,13 @@ import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.californium.observation.ObserveUtil;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.serialization.IdentitySerDes;
import org.eclipse.leshan.server.redis.serialization.ObservationSerDes;
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
import org.eclipse.leshan.server.registration.Deregistration;
@ -73,6 +75,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
private static final String REG_EP = "REG:EP:"; // (Endpoint => Registration)
private static final String REG_EP_REGID_IDX = "EP:REGID:"; // secondary index key (Registration ID => Endpoint)
private static final String REG_EP_ADDR_IDX = "EP:ADDR:"; // secondary index key (Socket Address => Endpoint)
private static final String REG_EP_IDENTITY = "EP:IDENTITY:"; // secondary index key (Identity => Endpoint)
private static final String LOCK_EP = "LOCK:EP:";
private static final byte[] OBS_TKN = "OBS:TKN:".getBytes(UTF_8);
private static final String OBS_TKNS_REGID_IDX = "TKNS:REGID:"; // secondary index (token list by registration)
@ -155,6 +158,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] addr_idx = toRegAddrKey(registration.getSocketAddress());
connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] identity_idx = toRegIdentityKey(registration.getIdentity());
connection.set(identity_idx, registration.getEndpoint().getBytes(UTF_8));
// Add or update expiration
addOrUpdateExpiration(connection, registration);
@ -167,6 +172,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) {
removeAddrIndex(connection, oldRegistration);
}
if (!oldRegistration.getIdentity().equals(registration.getIdentity())) {
removeIdentityIndex(connection, oldRegistration);
}
// remove old observation
Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, oldRegistration.getId());
@ -222,6 +230,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
removeAddrIndex(connection, r);
}
if (!r.getIdentity().equals(updatedRegistration.getIdentity())) {
removeIdentityIndex(connection, r);
}
return new UpdatedRegistration(r, updatedRegistration);
@ -268,6 +279,22 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
}
@Override
public Registration getRegistrationByIdentity(Identity identity) {
Validate.notNull(identity);
try (var connection = connectionFactory.getConnection()) {
byte[] ep = connection.get(toRegIdentityKey(identity));
if (ep == null) {
return null;
}
byte[] data = connection.get(toEndpointKey(ep));
if (data == null) {
return null;
}
return deserializeReg(data);
}
}
@Override
public Iterator<Registration> getAllRegistrations() {
try (var connection = connectionFactory.getConnection()) {
@ -325,6 +352,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.del(toEndpointKey(r.getEndpoint()));
Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, r.getId());
removeAddrIndex(connection, r);
removeIdentityIndex(connection, r);
removeExpiration(connection, r);
return new Deregistration(r, obsRemoved);
}
@ -337,20 +365,27 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
}
private void removeAddrIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegAddrKey(r.getSocketAddress()), r.getEndpoint());
}
private void removeIdentityIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegIdentityKey(r.getIdentity()), r.getEndpoint());
}
//TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies
private void removeAddrIndex(RedisConnection connection, Registration registration) {
private void removeSecondaryIndex(RedisConnection connection, byte[] indexKey, String endpointName) {
// Watch the key to remove.
byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress());
// connection.watch(regAddrKey);
// connection.watch(indexKey);
byte[] epFromAddr = connection.get(regAddrKey);
byte[] epFromAddr = connection.get(indexKey);
// Delete the key if needed.
if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) {
if (Arrays.equals(epFromAddr, endpointName.getBytes(UTF_8))) {
// Try to delete the key
// connection.multi();
connection.del(regAddrKey);
connection.del(indexKey);
// connection.exec();
// if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to
// if transaction failed this is not an issue as the index is probably reused and we don't need to
// delete it anymore.
} else {
// the key must not be deleted.
@ -374,6 +409,10 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort());
}
private byte[] toRegIdentityKey(Identity identity) {
return toKey(REG_EP_IDENTITY, IdentitySerDes.serialize(identity).toString());
}
private byte[] toEndpointKey(String endpoint) {
return toKey(REG_EP, endpoint);
}
@ -723,7 +762,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
@Override
public void run() {
try (var connection = connectionFactory.getConnection()) {
Set<byte[]> endpointsExpired = connection.zRangeByScore(EXP_EP, Double.NEGATIVE_INFINITY,
System.currentTimeMillis(), 0, cleanLimit);

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

@ -24,13 +24,14 @@ import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
private static final String SEC_EP = "SEC#EP#";
private static final String PSKID_SEC = "PSKID#SEC";
@ -72,73 +73,89 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
}
@Override
public Collection<SecurityInfo> getAll() {
try (var connection = connectionFactory.getConnection()) {
Collection<SecurityInfo> list = new LinkedList<>();
ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
List<Cursor<byte[]>> scans = new ArrayList<>();
if (connection instanceof RedisClusterConnection) {
((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
});
} else {
scans.add(connection.scan(scanOptions));
}
scans.forEach(scan -> {
scan.forEachRemaining(key -> {
byte[] element = connection.get(key);
list.add(deserialize(element));
});
});
return list;
}
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
//TODO: implement
}
@Override
public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
byte[] data = serialize(info);
try (var connection = connectionFactory.getConnection()) {
if (info.getIdentity() != null) {
// populate the secondary index (security info by PSK id)
String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
if (!oldEndpoint.equals(info.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
}
connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
}
byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
SecurityInfo previous = previousData == null ? null : deserialize(previousData);
String previousIdentity = previous == null ? null : previous.getIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
}
return previous;
}
public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
//TODO: implement
return null;
}
@Override
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
try (var connection = connectionFactory.getConnection()) {
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data != null) {
SecurityInfo info = deserialize(data);
if (info.getIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
}
connection.del((SEC_EP + endpoint).getBytes());
if (listener != null) {
listener.securityInfoRemoved(infosAreCompromised, info);
}
return info;
}
}
return null;
public void remove(String endpoint) {
//TODO: implement
}
// @Override
// public Collection<SecurityInfo> getAll() {
// try (var connection = connectionFactory.getConnection()) {
// Collection<SecurityInfo> list = new LinkedList<>();
// ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
// List<Cursor<byte[]>> scans = new ArrayList<>();
// if (connection instanceof RedisClusterConnection) {
// ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
// scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
// });
// } else {
// scans.add(connection.scan(scanOptions));
// }
//
// scans.forEach(scan -> {
// scan.forEachRemaining(key -> {
// byte[] element = connection.get(key);
// list.add(deserialize(element));
// });
// });
// return list;
// }
// }
//
// @Override
// public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
// byte[] data = serialize(info);
// try (var connection = connectionFactory.getConnection()) {
// if (info.getIdentity() != null) {
// // populate the secondary index (security info by PSK id)
// String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
// if (!oldEndpoint.equals(info.getEndpoint())) {
// throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
// }
// connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
// }
//
// byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
// SecurityInfo previous = previousData == null ? null : deserialize(previousData);
// String previousIdentity = previous == null ? null : previous.getIdentity();
// if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
// connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
// }
//
// return previous;
// }
// }
//
// @Override
// public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
// try (var connection = connectionFactory.getConnection()) {
// byte[] data = connection.get((SEC_EP + endpoint).getBytes());
//
// if (data != null) {
// SecurityInfo info = deserialize(data);
// if (info.getIdentity() != null) {
// connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
// }
// connection.del((SEC_EP + endpoint).getBytes());
// if (listener != null) {
// listener.securityInfoRemoved(infosAreCompromised, info);
// }
// return info;
// }
// }
// return null;
// }
private byte[] serialize(SecurityInfo secInfo) {
return SecurityInfoSerDes.serialize(secInfo);
}
@ -147,8 +164,4 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
return SecurityInfoSerDes.deserialize(data);
}
@Override
public void setListener(SecurityStoreListener listener) {
this.listener = listener;
}
}

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

@ -19,63 +19,39 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStore;
import org.eclipse.leshan.server.security.SecurityStoreListener;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import java.util.Collection;
@Slf4j
@Component
@TbLwM2mTransportComponent
public class TbLwM2mSecurityStore implements EditableSecurityStore {
public class TbLwM2mSecurityStore implements TbEditableSecurityStore {
private final LwM2mClientContext clientContext;
private final EditableSecurityStore securityStore;
private final TbEditableSecurityStore securityStore;
private final LwM2mCredentialsSecurityInfoValidator validator;
public TbLwM2mSecurityStore(LwM2mClientContext clientContext, EditableSecurityStore securityStore) {
this.clientContext = clientContext;
public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) {
this.securityStore = securityStore;
this.validator = validator;
}
@Override
public Collection<SecurityInfo> getAll() {
return securityStore.getAll();
}
@Override
public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
return securityStore.add(info);
}
@Override
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
return securityStore.remove(endpoint, infosAreCompromised);
}
@Override
public void setListener(SecurityStoreListener listener) {
securityStore.setListener(listener);
public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
return securityStore.getTbLwM2MSecurityInfoByEndpoint(endpoint);
}
@Override
public SecurityInfo getByEndpoint(String endpoint) {
SecurityInfo securityInfo = securityStore.getByEndpoint(endpoint);
if (securityInfo == null) {
LwM2mClient lwM2mClient = clientContext.getClientByEndpoint(endpoint);
if (lwM2mClient != null && lwM2mClient.getRegistration() != null && !lwM2mClient.getRegistration().getIdentity().isSecure()) {
return null;
}
securityInfo = clientContext.fetchClientByEndpoint(endpoint).getSecurityInfo();
try {
if (securityInfo != null) {
add(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
securityInfo = fetchAndPutSecurityInfo(endpoint);
}
return securityInfo;
}
@ -84,15 +60,31 @@ public class TbLwM2mSecurityStore implements EditableSecurityStore {
public SecurityInfo getByIdentity(String pskIdentity) {
SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity);
if (securityInfo == null) {
securityInfo = clientContext.fetchClientByEndpoint(pskIdentity).getSecurityInfo();
try {
if (securityInfo != null) {
add(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
securityInfo = fetchAndPutSecurityInfo(pskIdentity);
}
return securityInfo;
}
@Nullable
public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) {
TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
try {
if (securityInfo != null) {
securityStore.put(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
return securityInfo != null ? securityInfo.getSecurityInfo() : null;
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
securityStore.put(tbSecurityInfo);
}
@Override
public void remove(String endpoint) {
securityStore.remove(endpoint);
}
}

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

@ -17,17 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.InMemorySecurityStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import java.util.Optional;
@ -42,8 +39,7 @@ public class TbLwM2mStoreFactory {
private LwM2MTransportServerConfig config;
@Autowired
@Lazy
private LwM2mClientContext clientContext;
private LwM2mCredentialsSecurityInfoValidator validator;
@Value("${transport.lwm2m.redis.enabled:false}")
private boolean useRedis;
@ -55,9 +51,9 @@ public class TbLwM2mStoreFactory {
}
@Bean
private EditableSecurityStore securityStore() {
return new TbLwM2mSecurityStore(clientContext, redisConfiguration.isPresent() && useRedis ?
new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new InMemorySecurityStore());
private TbEditableSecurityStore securityStore() {
return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ?
new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator);
}
@Bean

25
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbSecurityStore.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.security.SecurityStore;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
public interface TbSecurityStore extends SecurityStore {
TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint);
}

12
pom.xml

@ -69,9 +69,7 @@
<jackson-core.version>2.12.1</jackson-core.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version>
<californium.version>2.6.1</californium.version>
<leshan-server.version>1.3.1</leshan-server.version>
<leshan-core.version>1.3.1</leshan-core.version>
<leshan-client.version>1.3.1</leshan-client.version>
<leshan.version>2.0.0-M3</leshan.version>
<gson.version>2.6.2</gson.version>
<freemarker.version>2.3.30</freemarker.version>
<mail.version>1.6.2</mail.version>
@ -1222,22 +1220,22 @@
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-server-cf</artifactId>
<version>${leshan-server.version}</version>
<version>${leshan.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-client-cf</artifactId>
<version>${leshan-client.version}</version>
<version>${leshan.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-server-redis</artifactId>
<version>${leshan-server.version}</version>
<version>${leshan.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-core</artifactId>
<version>${leshan-core.version}</version>
<version>${leshan.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.californium</groupId>

Loading…
Cancel
Save