Commit d451768302ecfe8ee999288006c6af820478c7f1

Authored by Andrew Shvayka
Committed by GitHub
2 parents 3d3cb1f1 51f922ad

Merge pull request #4711 from AndrewVolosytnykhThingsboard/lwm2m-refactoring

Redis Security Store implementation
Showing 16 changed files with 351 additions and 340 deletions
@@ -17,24 +17,47 @@ package org.thingsboard.server.transport.lwm2m; @@ -17,24 +17,47 @@ package org.thingsboard.server.transport.lwm2m;
17 17
18 import com.fasterxml.jackson.core.type.TypeReference; 18 import com.fasterxml.jackson.core.type.TypeReference;
19 import org.apache.commons.io.IOUtils; 19 import org.apache.commons.io.IOUtils;
  20 +import org.eclipse.californium.core.network.config.NetworkConfig;
  21 +import org.eclipse.leshan.client.object.Security;
20 import org.eclipse.leshan.core.util.Hex; 22 import org.eclipse.leshan.core.util.Hex;
  23 +import org.jetbrains.annotations.NotNull;
21 import org.junit.After; 24 import org.junit.After;
22 import org.junit.Assert; 25 import org.junit.Assert;
23 import org.junit.Before; 26 import org.junit.Before;
  27 +import org.springframework.mock.web.MockMultipartFile;
  28 +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
  29 +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
24 import org.thingsboard.common.util.JacksonUtil; 30 import org.thingsboard.common.util.JacksonUtil;
  31 +import org.thingsboard.server.common.data.Device;
25 import org.thingsboard.server.common.data.DeviceProfile; 32 import org.thingsboard.server.common.data.DeviceProfile;
26 import org.thingsboard.server.common.data.DeviceProfileProvisionType; 33 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
27 import org.thingsboard.server.common.data.DeviceProfileType; 34 import org.thingsboard.server.common.data.DeviceProfileType;
28 import org.thingsboard.server.common.data.DeviceTransportType; 35 import org.thingsboard.server.common.data.DeviceTransportType;
  36 +import org.thingsboard.server.common.data.OtaPackageInfo;
29 import org.thingsboard.server.common.data.ResourceType; 37 import org.thingsboard.server.common.data.ResourceType;
30 import org.thingsboard.server.common.data.TbResource; 38 import org.thingsboard.server.common.data.TbResource;
  39 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
31 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; 40 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
32 import org.thingsboard.server.common.data.device.profile.DeviceProfileData; 41 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
33 import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; 42 import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
34 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; 43 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
  44 +import org.thingsboard.server.common.data.query.EntityData;
  45 +import org.thingsboard.server.common.data.query.EntityDataPageLink;
  46 +import org.thingsboard.server.common.data.query.EntityDataQuery;
  47 +import org.thingsboard.server.common.data.query.EntityKey;
  48 +import org.thingsboard.server.common.data.query.EntityKeyType;
  49 +import org.thingsboard.server.common.data.query.SingleEntityFilter;
  50 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  51 +import org.thingsboard.server.common.data.security.DeviceCredentialsType;
35 import org.thingsboard.server.controller.AbstractWebsocketTest; 52 import org.thingsboard.server.controller.AbstractWebsocketTest;
36 import org.thingsboard.server.controller.TbTestWebSocketClient; 53 import org.thingsboard.server.controller.TbTestWebSocketClient;
37 import org.thingsboard.server.dao.service.DaoSqlTest; 54 import org.thingsboard.server.dao.service.DaoSqlTest;
  55 +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
  56 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
  57 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
  58 +import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
  59 +import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
  60 +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
38 61
39 import java.io.IOException; 62 import java.io.IOException;
40 import java.io.InputStream; 63 import java.io.InputStream;
@@ -54,9 +77,15 @@ import java.security.spec.ECPrivateKeySpec; @@ -54,9 +77,15 @@ import java.security.spec.ECPrivateKeySpec;
54 import java.security.spec.ECPublicKeySpec; 77 import java.security.spec.ECPublicKeySpec;
55 import java.security.spec.KeySpec; 78 import java.security.spec.KeySpec;
56 import java.util.Base64; 79 import java.util.Base64;
  80 +import java.util.Collections;
  81 +import java.util.List;
57 import java.util.concurrent.Executors; 82 import java.util.concurrent.Executors;
58 import java.util.concurrent.ScheduledExecutorService; 83 import java.util.concurrent.ScheduledExecutorService;
59 84
  85 +import static org.eclipse.leshan.client.object.Security.noSec;
  86 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  87 +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
  88 +
60 @DaoSqlTest 89 @DaoSqlTest
61 public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { 90 public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
62 91
@@ -139,6 +168,15 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -139,6 +168,15 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
139 // certificates trustedby the server (should contain rootCA) 168 // certificates trustedby the server (should contain rootCA)
140 protected final Certificate[] trustedCertificates = new Certificate[1]; 169 protected final Certificate[] trustedCertificates = new Certificate[1];
141 170
  171 + protected static final int SECURE_PORT = 5686;
  172 + protected static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT));
  173 + protected static final String ENDPOINT = "deviceAEndpoint";
  174 + protected static final String SECURE_URI = "coaps://localhost:" + SECURE_PORT;
  175 +
  176 + protected static final int PORT = 5685;
  177 + protected static final Security SECURITY = noSec("coap://localhost:" + PORT, 123);
  178 + protected static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
  179 +
142 public AbstractLwM2MIntegrationTest() { 180 public AbstractLwM2MIntegrationTest() {
143 // create client credentials 181 // create client credentials
144 try { 182 try {
@@ -262,10 +300,95 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -262,10 +300,95 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
262 Assert.assertNotNull(deviceProfile); 300 Assert.assertNotNull(deviceProfile);
263 } 301 }
264 302
  303 + @NotNull
  304 + protected Device createDevice(LwM2MClientCredentials clientCredentials) throws Exception {
  305 + Device device = new Device();
  306 + device.setName("Device A");
  307 + device.setDeviceProfileId(deviceProfile.getId());
  308 + device.setTenantId(tenantId);
  309 + device = doPost("/api/device", device, Device.class);
  310 + Assert.assertNotNull(device);
  311 +
  312 + DeviceCredentials deviceCredentials =
  313 + doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  314 + Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());
  315 + deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
  316 +
  317 + LwM2MCredentials credentials = new LwM2MCredentials();
  318 +
  319 + credentials.setClient(clientCredentials);
  320 +
  321 + deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials));
  322 + doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
  323 + return device;
  324 + }
  325 +
  326 +
  327 + protected OtaPackageInfo createFirmware() throws Exception {
  328 + String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a";
  329 +
  330 + OtaPackageInfo firmwareInfo = new OtaPackageInfo();
  331 + firmwareInfo.setDeviceProfileId(deviceProfile.getId());
  332 + firmwareInfo.setType(FIRMWARE);
  333 + firmwareInfo.setTitle("My firmware");
  334 + firmwareInfo.setVersion("v1.0");
  335 +
  336 + OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
  337 +
  338 + MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1});
  339 +
  340 + return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256");
  341 + }
  342 +
  343 + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
  344 + MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
  345 + postRequest.file(content);
  346 + setJwtToken(postRequest);
  347 + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class);
  348 + }
  349 +
265 @After 350 @After
266 public void after() { 351 public void after() {
267 executor.shutdownNow(); 352 executor.shutdownNow();
268 wsClient.close(); 353 wsClient.close();
269 } 354 }
270 355
  356 + public void basicTestConnectionObserveTelemetry(Security security,
  357 + LwM2MClientCredentials credentials,
  358 + NetworkConfig coapConfig,
  359 + String endpoint) throws Exception {
  360 + createDeviceProfile(TRANSPORT_CONFIGURATION);
  361 + Device device = createDevice(credentials);
  362 +
  363 + SingleEntityFilter sef = new SingleEntityFilter();
  364 + sef.setSingleEntity(device.getId());
  365 + LatestValueCmd latestCmd = new LatestValueCmd();
  366 + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
  367 + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
  368 + Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
  369 +
  370 + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
  371 + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
  372 + wrapper.setEntityDataCmds(Collections.singletonList(cmd));
  373 +
  374 + wsClient.send(mapper.writeValueAsString(wrapper));
  375 + wsClient.waitForReply();
  376 +
  377 + wsClient.registerWaitForUpdate();
  378 + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
  379 +
  380 + client.init(security, coapConfig);
  381 + String msg = wsClient.waitForUpdate();
  382 +
  383 + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
  384 + Assert.assertEquals(1, update.getCmdId());
  385 + List<EntityData> eData = update.getUpdate();
  386 + Assert.assertNotNull(eData);
  387 + Assert.assertEquals(1, eData.size());
  388 + Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
  389 + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
  390 + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
  391 + Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
  392 + client.destroy();
  393 + }
271 } 394 }
@@ -15,14 +15,8 @@ @@ -15,14 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m; 16 package org.thingsboard.server.transport.lwm2m;
17 17
18 -import org.eclipse.californium.core.network.config.NetworkConfig;  
19 -import org.eclipse.leshan.client.object.Security;  
20 import org.junit.Assert; 18 import org.junit.Assert;
21 import org.junit.Test; 19 import org.junit.Test;
22 -import org.springframework.mock.web.MockMultipartFile;  
23 -import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;  
24 -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;  
25 -import org.thingsboard.common.util.JacksonUtil;  
26 import org.thingsboard.server.common.data.Device; 20 import org.thingsboard.server.common.data.Device;
27 import org.thingsboard.server.common.data.OtaPackageInfo; 21 import org.thingsboard.server.common.data.OtaPackageInfo;
28 import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; 22 import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
@@ -32,116 +26,30 @@ import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -32,116 +26,30 @@ import org.thingsboard.server.common.data.query.EntityDataQuery;
32 import org.thingsboard.server.common.data.query.EntityKey; 26 import org.thingsboard.server.common.data.query.EntityKey;
33 import org.thingsboard.server.common.data.query.EntityKeyType; 27 import org.thingsboard.server.common.data.query.EntityKeyType;
34 import org.thingsboard.server.common.data.query.SingleEntityFilter; 28 import org.thingsboard.server.common.data.query.SingleEntityFilter;
35 -import org.thingsboard.server.common.data.security.DeviceCredentials;  
36 -import org.thingsboard.server.common.data.security.DeviceCredentialsType;  
37 import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; 29 import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
38 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 30 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
39 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; 31 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
40 import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; 32 import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
41 import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; 33 import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
42 -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;  
43 34
44 import java.util.Collections; 35 import java.util.Collections;
45 import java.util.List; 36 import java.util.List;
46 37
47 -import static org.eclipse.leshan.client.object.Security.noSec;  
48 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
49 -import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;  
50 -  
51 public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { 38 public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
52 39
53 - private final int PORT = 5685;  
54 - private final Security SECURITY = noSec("coap://localhost:" + PORT, 123);  
55 - private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));  
56 - private final String ENDPOINT = "noSecEndpoint";  
57 -  
58 - private Device createDevice() throws Exception {  
59 - Device device = new Device();  
60 - device.setName("Device A");  
61 - device.setDeviceProfileId(deviceProfile.getId());  
62 - device.setTenantId(tenantId);  
63 - device = doPost("/api/device", device, Device.class);  
64 - Assert.assertNotNull(device);  
65 -  
66 - DeviceCredentials deviceCredentials =  
67 - doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);  
68 - Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());  
69 - deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);  
70 -  
71 - LwM2MCredentials noSecCredentials = new LwM2MCredentials();  
72 - NoSecClientCredentials clientCredentials = new NoSecClientCredentials();  
73 - clientCredentials.setEndpoint(ENDPOINT);  
74 - noSecCredentials.setClient(clientCredentials);  
75 - deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials));  
76 - doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());  
77 - return device;  
78 - }  
79 -  
80 - private OtaPackageInfo createFirmware() throws Exception {  
81 - String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a";  
82 -  
83 - OtaPackageInfo firmwareInfo = new OtaPackageInfo();  
84 - firmwareInfo.setDeviceProfileId(deviceProfile.getId());  
85 - firmwareInfo.setType(FIRMWARE);  
86 - firmwareInfo.setTitle("My firmware");  
87 - firmwareInfo.setVersion("v1.0");  
88 -  
89 - OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);  
90 -  
91 - MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1});  
92 -  
93 - return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256");  
94 - }  
95 -  
96 - protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {  
97 - MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);  
98 - postRequest.file(content);  
99 - setJwtToken(postRequest);  
100 - return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class);  
101 - }  
102 -  
103 @Test 40 @Test
104 public void testConnectAndObserveTelemetry() throws Exception { 41 public void testConnectAndObserveTelemetry() throws Exception {
105 - createDeviceProfile(TRANSPORT_CONFIGURATION);  
106 -  
107 - Device device = createDevice();  
108 -  
109 - SingleEntityFilter sef = new SingleEntityFilter();  
110 - sef.setSingleEntity(device.getId());  
111 - LatestValueCmd latestCmd = new LatestValueCmd();  
112 - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));  
113 - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),  
114 - Collections.emptyList(), Collections.emptyList(), Collections.emptyList());  
115 -  
116 - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);  
117 - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();  
118 - wrapper.setEntityDataCmds(Collections.singletonList(cmd));  
119 -  
120 - wsClient.send(mapper.writeValueAsString(wrapper));  
121 - wsClient.waitForReply();  
122 -  
123 - wsClient.registerWaitForUpdate();  
124 - LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT);  
125 - client.init(SECURITY, COAP_CONFIG);  
126 - String msg = wsClient.waitForUpdate();  
127 -  
128 - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);  
129 - Assert.assertEquals(1, update.getCmdId());  
130 - List<EntityData> eData = update.getUpdate();  
131 - Assert.assertNotNull(eData);  
132 - Assert.assertEquals(1, eData.size());  
133 - Assert.assertEquals(device.getId(), eData.get(0).getEntityId());  
134 - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));  
135 - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");  
136 - Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));  
137 - client.destroy(); 42 + NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
  43 + clientCredentials.setEndpoint(ENDPOINT);
  44 + super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT);
138 } 45 }
139 46
140 @Test 47 @Test
141 public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception { 48 public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception {
142 createDeviceProfile(TRANSPORT_CONFIGURATION); 49 createDeviceProfile(TRANSPORT_CONFIGURATION);
143 -  
144 - Device device = createDevice(); 50 + NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
  51 + clientCredentials.setEndpoint(ENDPOINT);
  52 + Device device = createDevice(clientCredentials);
145 53
146 OtaPackageInfo firmware = createFirmware(); 54 OtaPackageInfo firmware = createFirmware();
147 55
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m;
  17 +
  18 +import org.eclipse.leshan.client.object.Security;
  19 +import org.eclipse.leshan.core.util.Hex;
  20 +import org.junit.Test;
  21 +import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
  22 +
  23 +import java.nio.charset.StandardCharsets;
  24 +
  25 +import static org.eclipse.leshan.client.object.Security.psk;
  26 +
  27 +public class PskLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest {
  28 +
  29 + @Test
  30 + public void testConnectWithPSKAndObserveTelemetry() throws Exception {
  31 + String pskIdentity = "SOME_PSK_ID";
  32 + String pskKey = "73656372657450534b";
  33 + PSKClientCredentials clientCredentials = new PSKClientCredentials();
  34 + clientCredentials.setEndpoint(ENDPOINT);
  35 + clientCredentials.setKey(pskKey);
  36 + clientCredentials.setIdentity(pskIdentity);
  37 + Security security = psk(SECURE_URI,
  38 + 123,
  39 + pskIdentity.getBytes(StandardCharsets.UTF_8),
  40 + Hex.decodeHex(pskKey.toCharArray()));
  41 + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, ENDPOINT);
  42 + }
  43 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m;
  17 +
  18 +import org.eclipse.leshan.client.object.Security;
  19 +import org.eclipse.leshan.core.util.Hex;
  20 +import org.junit.Test;
  21 +import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
  22 +
  23 +import static org.eclipse.leshan.client.object.Security.rpk;
  24 +
  25 +public class RpkLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
  26 + @Test
  27 + public void testConnectWithRPKAndObserveTelemetry() throws Exception {
  28 + RPKClientCredentials rpkClientCredentials = new RPKClientCredentials();
  29 + rpkClientCredentials.setEndpoint(ENDPOINT);
  30 + rpkClientCredentials.setKey(Hex.encodeHexString(clientPublicKey.getEncoded()));
  31 + Security security = rpk(SECURE_URI,
  32 + 123,
  33 + clientPublicKey.getEncoded(),
  34 + clientPrivateKey.getEncoded(),
  35 + serverX509Cert.getPublicKey().getEncoded());
  36 + super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, ENDPOINT);
  37 + }
  38 +
  39 +}
@@ -15,145 +15,38 @@ @@ -15,145 +15,38 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m; 16 package org.thingsboard.server.transport.lwm2m;
17 17
18 -import org.eclipse.californium.core.network.config.NetworkConfig;  
19 import org.eclipse.leshan.client.object.Security; 18 import org.eclipse.leshan.client.object.Security;
20 -import org.jetbrains.annotations.NotNull;  
21 -import org.junit.Assert;  
22 -import org.junit.Ignore;  
23 import org.junit.Test; 19 import org.junit.Test;
24 -import org.thingsboard.common.util.JacksonUtil;  
25 -import org.thingsboard.server.common.data.Device;  
26 import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; 20 import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
27 -import org.thingsboard.server.common.data.query.EntityData;  
28 -import org.thingsboard.server.common.data.query.EntityDataPageLink;  
29 -import org.thingsboard.server.common.data.query.EntityDataQuery;  
30 -import org.thingsboard.server.common.data.query.EntityKey;  
31 -import org.thingsboard.server.common.data.query.EntityKeyType;  
32 -import org.thingsboard.server.common.data.query.SingleEntityFilter;  
33 -import org.thingsboard.server.common.data.security.DeviceCredentials;  
34 -import org.thingsboard.server.common.data.security.DeviceCredentialsType;  
35 import org.thingsboard.server.common.transport.util.SslUtil; 21 import org.thingsboard.server.common.transport.util.SslUtil;
36 -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;  
37 -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;  
38 -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;  
39 -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;  
40 -import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;  
41 -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;  
42 -  
43 -import java.util.Collections;  
44 -import java.util.List;  
45 22
46 import static org.eclipse.leshan.client.object.Security.x509; 23 import static org.eclipse.leshan.client.object.Security.x509;
47 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
48 24
49 public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { 25 public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
50 26
51 - private final int port = 5686;  
52 - private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port));  
53 - private final String endpoint = "deviceAEndpoint";  
54 - private final String serverUri = "coaps://localhost:" + port;  
55 -  
56 - private Device createDevice(X509ClientCredentials clientCredentials) throws Exception {  
57 - Device device = new Device();  
58 - device.setName("Device A");  
59 - device.setDeviceProfileId(deviceProfile.getId());  
60 - device.setTenantId(tenantId);  
61 - device = doPost("/api/device", device, Device.class);  
62 - Assert.assertNotNull(device);  
63 -  
64 - DeviceCredentials deviceCredentials =  
65 - doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);  
66 - Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());  
67 - deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);  
68 -  
69 - LwM2MCredentials credentials = new LwM2MCredentials();  
70 -  
71 - credentials.setClient(clientCredentials);  
72 -  
73 - deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials));  
74 - doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());  
75 - return device;  
76 - }  
77 -  
78 @Test 27 @Test
79 public void testConnectAndObserveTelemetry() throws Exception { 28 public void testConnectAndObserveTelemetry() throws Exception {
80 - createDeviceProfile(TRANSPORT_CONFIGURATION);  
81 X509ClientCredentials credentials = new X509ClientCredentials(); 29 X509ClientCredentials credentials = new X509ClientCredentials();
82 - credentials.setEndpoint(endpoint);  
83 - Device device = createDevice(credentials);  
84 -  
85 - SingleEntityFilter sef = new SingleEntityFilter();  
86 - sef.setSingleEntity(device.getId());  
87 - LatestValueCmd latestCmd = new LatestValueCmd();  
88 - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));  
89 - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),  
90 - Collections.emptyList(), Collections.emptyList(), Collections.emptyList());  
91 -  
92 - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);  
93 - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();  
94 - wrapper.setEntityDataCmds(Collections.singletonList(cmd));  
95 -  
96 - wsClient.send(mapper.writeValueAsString(wrapper));  
97 - wsClient.waitForReply();  
98 -  
99 - wsClient.registerWaitForUpdate();  
100 - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);  
101 - Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());  
102 - client.init(security, coapConfig);  
103 - String msg = wsClient.waitForUpdate();  
104 -  
105 - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);  
106 - Assert.assertEquals(1, update.getCmdId());  
107 - List<EntityData> eData = update.getUpdate();  
108 - Assert.assertNotNull(eData);  
109 - Assert.assertEquals(1, eData.size());  
110 - Assert.assertEquals(device.getId(), eData.get(0).getEntityId());  
111 - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));  
112 - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");  
113 - Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));  
114 - client.destroy(); 30 + credentials.setEndpoint(ENDPOINT);
  31 + Security security = x509(SECURE_URI,
  32 + 123,
  33 + clientX509Cert.getEncoded(),
  34 + clientPrivateKeyFromCert.getEncoded(),
  35 + serverX509Cert.getEncoded());
  36 + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT);
115 } 37 }
116 38
117 @Test 39 @Test
118 public void testConnectWithCertAndObserveTelemetry() throws Exception { 40 public void testConnectWithCertAndObserveTelemetry() throws Exception {
119 - createDeviceProfile(TRANSPORT_CONFIGURATION);  
120 X509ClientCredentials credentials = new X509ClientCredentials(); 41 X509ClientCredentials credentials = new X509ClientCredentials();
121 - credentials.setEndpoint(endpoint); 42 + credentials.setEndpoint(ENDPOINT);
122 credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); 43 credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted));
123 - Device device = createDevice(credentials);  
124 -  
125 - SingleEntityFilter sef = new SingleEntityFilter();  
126 - sef.setSingleEntity(device.getId());  
127 - LatestValueCmd latestCmd = new LatestValueCmd();  
128 - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));  
129 - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),  
130 - Collections.emptyList(), Collections.emptyList(), Collections.emptyList());  
131 -  
132 - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);  
133 - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();  
134 - wrapper.setEntityDataCmds(Collections.singletonList(cmd));  
135 -  
136 - wsClient.send(mapper.writeValueAsString(wrapper));  
137 - wsClient.waitForReply();  
138 -  
139 - wsClient.registerWaitForUpdate();  
140 - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);  
141 -  
142 - Security security = x509(serverUri, 123, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());  
143 -  
144 - client.init(security, coapConfig);  
145 - String msg = wsClient.waitForUpdate();  
146 -  
147 - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);  
148 - Assert.assertEquals(1, update.getCmdId());  
149 - List<EntityData> eData = update.getUpdate();  
150 - Assert.assertNotNull(eData);  
151 - Assert.assertEquals(1, eData.size());  
152 - Assert.assertEquals(device.getId(), eData.get(0).getEntityId());  
153 - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));  
154 - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");  
155 - Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));  
156 - client.destroy(); 44 + Security security = x509(SECURE_URI,
  45 + 123,
  46 + clientX509CertNotTrusted.getEncoded(),
  47 + clientPrivateKeyFromCert.getEncoded(),
  48 + serverX509Cert.getEncoded());
  49 + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT);
157 } 50 }
158 51
159 } 52 }
common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/AbstractLwM2MClientCredentialsWithKey.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java
@@ -15,20 +15,25 @@ @@ -15,20 +15,25 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.credentials.lwm2m; 16 package org.thingsboard.server.common.data.device.credentials.lwm2m;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Getter;
  20 +import lombok.Setter;
18 import lombok.SneakyThrows; 21 import lombok.SneakyThrows;
19 import org.apache.commons.codec.binary.Hex; 22 import org.apache.commons.codec.binary.Hex;
20 23
21 -public abstract class HasKey extends AbstractLwM2MClientCredentials {  
22 - private byte[] key; 24 +public abstract class AbstractLwM2MClientCredentialsWithKey extends AbstractLwM2MClientCredentials {
  25 + @Getter
  26 + @Setter
  27 + private String key;
  28 +
  29 + private byte[] keyInBytes;
23 30
24 @SneakyThrows 31 @SneakyThrows
25 - public void setKey(String key) {  
26 - if (key != null) {  
27 - this.key = Hex.decodeHex(key.toLowerCase().toCharArray()); 32 + @JsonIgnore
  33 + public byte[] getDecodedKey() {
  34 + if (keyInBytes == null) {
  35 + keyInBytes = Hex.decodeHex(key.toLowerCase().toCharArray());
28 } 36 }
29 - }  
30 -  
31 - public byte[] getKey() {  
32 - return key; 37 + return keyInBytes;
33 } 38 }
34 } 39 }
@@ -20,7 +20,7 @@ import lombok.Setter; @@ -20,7 +20,7 @@ import lombok.Setter;
20 20
21 @Getter 21 @Getter
22 @Setter 22 @Setter
23 -public class PSKClientCredentials extends HasKey { 23 +public class PSKClientCredentials extends AbstractLwM2MClientCredentialsWithKey {
24 private String identity; 24 private String identity;
25 25
26 @Override 26 @Override
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.credentials.lwm2m; 16 package org.thingsboard.server.common.data.device.credentials.lwm2m;
17 17
18 -public class RPKClientCredentials extends HasKey { 18 +public class RPKClientCredentials extends AbstractLwM2MClientCredentialsWithKey {
19 19
20 @Override 20 @Override
21 public LwM2MSecurityMode getSecurityConfigClientMode() { 21 public LwM2MSecurityMode getSecurityConfigClientMode() {
@@ -21,10 +21,11 @@ import org.eclipse.leshan.core.request.BindingMode; @@ -21,10 +21,11 @@ import org.eclipse.leshan.core.request.BindingMode;
21 import org.eclipse.leshan.core.util.Hex; 21 import org.eclipse.leshan.core.util.Hex;
22 import org.eclipse.leshan.server.bootstrap.BootstrapConfig; 22 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
23 23
  24 +import java.io.Serializable;
24 import java.nio.charset.StandardCharsets; 25 import java.nio.charset.StandardCharsets;
25 26
26 @Data 27 @Data
27 -public class LwM2MBootstrapConfig { 28 +public class LwM2MBootstrapConfig implements Serializable {
28 /* 29 /*
29 interface BootstrapSecurityConfig 30 interface BootstrapSecurityConfig
30 servers: BootstrapServersSecurityConfig, 31 servers: BootstrapServersSecurityConfig,
@@ -138,10 +138,10 @@ public class LwM2mCredentialsSecurityInfoValidator { @@ -138,10 +138,10 @@ public class LwM2mCredentialsSecurityInfoValidator {
138 PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig; 138 PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
139 if (StringUtils.isNotEmpty(pskConfig.getIdentity())) { 139 if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
140 try { 140 try {
141 - if (pskConfig.getKey() != null && pskConfig.getKey().length > 0) { 141 + if (pskConfig.getDecodedKey() != null && pskConfig.getDecodedKey().length > 0) {
142 endpoint = StringUtils.isNotEmpty(pskConfig.getEndpoint()) ? pskConfig.getEndpoint() : endpoint; 142 endpoint = StringUtils.isNotEmpty(pskConfig.getEndpoint()) ? pskConfig.getEndpoint() : endpoint;
143 if (endpoint != null && !endpoint.isEmpty()) { 143 if (endpoint != null && !endpoint.isEmpty()) {
144 - result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getKey())); 144 + result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getDecodedKey()));
145 result.setSecurityMode(PSK); 145 result.setSecurityMode(PSK);
146 } 146 }
147 } 147 }
@@ -156,8 +156,8 @@ public class LwM2mCredentialsSecurityInfoValidator { @@ -156,8 +156,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
156 private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { 156 private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
157 RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig; 157 RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
158 try { 158 try {
159 - if (rpkConfig.getKey() != null) {  
160 - PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getKey()); 159 + if (rpkConfig.getDecodedKey() != null) {
  160 + PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getDecodedKey());
161 result.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(endpoint, key)); 161 result.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(endpoint, key));
162 result.setSecurityMode(RPK); 162 result.setSecurityMode(RPK);
163 } else { 163 } else {
@@ -23,8 +23,10 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -23,8 +23,10 @@ import org.thingsboard.server.common.data.DeviceProfile;
23 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 23 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
24 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; 24 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
25 25
  26 +import java.io.Serializable;
  27 +
26 @Data 28 @Data
27 -public class TbLwM2MSecurityInfo { 29 +public class TbLwM2MSecurityInfo implements Serializable {
28 private ValidateDeviceCredentialsResponse msg; 30 private ValidateDeviceCredentialsResponse msg;
29 private SecurityInfo securityInfo; 31 private SecurityInfo securityInfo;
30 private SecurityMode securityMode; 32 private SecurityMode securityMode;
@@ -18,8 +18,10 @@ package org.thingsboard.server.transport.lwm2m.secure; @@ -18,8 +18,10 @@ package org.thingsboard.server.transport.lwm2m.secure;
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 19 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
20 20
  21 +import java.io.Serializable;
  22 +
21 @Data 23 @Data
22 -public class TbX509DtlsSessionInfo { 24 +public class TbX509DtlsSessionInfo implements Serializable {
23 25
24 private final String x509CommonName; 26 private final String x509CommonName;
25 private final ValidateDeviceCredentialsResponse credentials; 27 private final ValidateDeviceCredentialsResponse credentials;
@@ -15,28 +15,29 @@ @@ -15,28 +15,29 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m.server.store; 16 package org.thingsboard.server.transport.lwm2m.server.store;
17 17
18 -import com.fasterxml.jackson.databind.JsonNode; 18 +import org.nustaq.serialization.FSTConfiguration;
19 import org.springframework.data.redis.connection.RedisConnectionFactory; 19 import org.springframework.data.redis.connection.RedisConnectionFactory;
20 -import org.thingsboard.common.util.JacksonUtil;  
21 import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo; 20 import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
22 21
23 public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore { 22 public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore {
24 23
25 private static final String SESSION_EP = "SESSION#EP#"; 24 private static final String SESSION_EP = "SESSION#EP#";
26 - RedisConnectionFactory connectionFactory; 25 + private final RedisConnectionFactory connectionFactory;
  26 + private final FSTConfiguration serializer;
27 27
28 public TbLwM2MDtlsSessionRedisStore(RedisConnectionFactory redisConnectionFactory) { 28 public TbLwM2MDtlsSessionRedisStore(RedisConnectionFactory redisConnectionFactory) {
29 this.connectionFactory = redisConnectionFactory; 29 this.connectionFactory = redisConnectionFactory;
  30 + this.serializer = FSTConfiguration.createDefaultConfiguration();
30 } 31 }
31 32
32 @Override 33 @Override
33 public void put(String endpoint, TbX509DtlsSessionInfo msg) { 34 public void put(String endpoint, TbX509DtlsSessionInfo msg) {
34 try (var c = connectionFactory.getConnection()) { 35 try (var c = connectionFactory.getConnection()) {
35 - var msgJson = JacksonUtil.convertValue(msg, JsonNode.class);  
36 - if (msgJson != null) {  
37 - c.set(getKey(endpoint), msgJson.toString().getBytes()); 36 + var serializedMsg = serializer.asByteArray(msg);
  37 + if (serializedMsg != null) {
  38 + c.set(getKey(endpoint), serializedMsg);
38 } else { 39 } else {
39 - throw new RuntimeException("Problem with serialization of message: " + msg.toString()); 40 + throw new RuntimeException("Problem with serialization of message: " + msg);
40 } 41 }
41 } 42 }
42 } 43 }
@@ -46,7 +47,7 @@ public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore { @@ -46,7 +47,7 @@ public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore {
46 try (var c = connectionFactory.getConnection()) { 47 try (var c = connectionFactory.getConnection()) {
47 var data = c.get(getKey(endpoint)); 48 var data = c.get(getKey(endpoint));
48 if (data != null) { 49 if (data != null) {
49 - return JacksonUtil.fromString(new String(data), TbX509DtlsSessionInfo.class); 50 + return (TbX509DtlsSessionInfo) serializer.asObject(data);
50 } else { 51 } else {
51 return null; 52 return null;
52 } 53 }
@@ -15,49 +15,55 @@ @@ -15,49 +15,55 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m.server.store; 16 package org.thingsboard.server.transport.lwm2m.server.store;
17 17
18 -import org.eclipse.leshan.server.redis.serialization.SecurityInfoSerDes;  
19 -import org.eclipse.leshan.server.security.EditableSecurityStore;  
20 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; 18 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
21 import org.eclipse.leshan.server.security.SecurityInfo; 19 import org.eclipse.leshan.server.security.SecurityInfo;
22 -import org.eclipse.leshan.server.security.SecurityStoreListener;  
23 -import org.springframework.data.redis.connection.RedisClusterConnection; 20 +import org.nustaq.serialization.FSTConfiguration;
24 import org.springframework.data.redis.connection.RedisConnectionFactory; 21 import org.springframework.data.redis.connection.RedisConnectionFactory;
25 -import org.springframework.data.redis.core.Cursor;  
26 -import org.springframework.data.redis.core.ScanOptions; 22 +import org.springframework.integration.redis.util.RedisLockRegistry;
27 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; 23 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
28 24
29 -import java.util.ArrayList;  
30 -import java.util.Collection;  
31 -import java.util.LinkedList;  
32 -import java.util.List; 25 +import java.util.concurrent.locks.Lock;
33 26
34 public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore { 27 public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
35 private static final String SEC_EP = "SEC#EP#"; 28 private static final String SEC_EP = "SEC#EP#";
36 - 29 + private static final String LOCK_EP = "LOCK#EP#";
37 private static final String PSKID_SEC = "PSKID#SEC"; 30 private static final String PSKID_SEC = "PSKID#SEC";
38 31
39 private final RedisConnectionFactory connectionFactory; 32 private final RedisConnectionFactory connectionFactory;
40 - private SecurityStoreListener listener; 33 + private final FSTConfiguration serializer;
  34 + private final RedisLockRegistry redisLock;
41 35
42 public TbLwM2mRedisSecurityStore(RedisConnectionFactory connectionFactory) { 36 public TbLwM2mRedisSecurityStore(RedisConnectionFactory connectionFactory) {
43 this.connectionFactory = connectionFactory; 37 this.connectionFactory = connectionFactory;
  38 + redisLock = new RedisLockRegistry(connectionFactory, "Security");
  39 + serializer = FSTConfiguration.createDefaultConfiguration();
44 } 40 }
45 41
46 @Override 42 @Override
47 public SecurityInfo getByEndpoint(String endpoint) { 43 public SecurityInfo getByEndpoint(String endpoint) {
  44 + Lock lock = null;
48 try (var connection = connectionFactory.getConnection()) { 45 try (var connection = connectionFactory.getConnection()) {
  46 + lock = redisLock.obtain(toLockKey(endpoint));
  47 + lock.lock();
49 byte[] data = connection.get((SEC_EP + endpoint).getBytes()); 48 byte[] data = connection.get((SEC_EP + endpoint).getBytes());
50 if (data == null) { 49 if (data == null) {
51 return null; 50 return null;
52 } else { 51 } else {
53 - return deserialize(data); 52 + return ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo();
  53 + }
  54 + } finally {
  55 + if (lock != null) {
  56 + lock.unlock();
54 } 57 }
55 } 58 }
56 } 59 }
57 60
58 @Override 61 @Override
59 public SecurityInfo getByIdentity(String identity) { 62 public SecurityInfo getByIdentity(String identity) {
  63 + Lock lock = null;
60 try (var connection = connectionFactory.getConnection()) { 64 try (var connection = connectionFactory.getConnection()) {
  65 + lock = redisLock.obtain(toLockKey(identity));
  66 + lock.lock();
61 byte[] ep = connection.hGet(PSKID_SEC.getBytes(), identity.getBytes()); 67 byte[] ep = connection.hGet(PSKID_SEC.getBytes(), identity.getBytes());
62 if (ep == null) { 68 if (ep == null) {
63 return null; 69 return null;
@@ -66,102 +72,86 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore { @@ -66,102 +72,86 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
66 if (data == null) { 72 if (data == null) {
67 return null; 73 return null;
68 } else { 74 } else {
69 - return deserialize(data); 75 + return ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo();
70 } 76 }
71 } 77 }
  78 + } finally {
  79 + if (lock != null) {
  80 + lock.unlock();
  81 + }
72 } 82 }
73 } 83 }
74 84
75 @Override 85 @Override
76 public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException { 86 public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
77 - //TODO: implement 87 + SecurityInfo info = tbSecurityInfo.getSecurityInfo();
  88 + byte[] tbSecurityInfoSerialized = serializer.asByteArray(tbSecurityInfo);
  89 + Lock lock = null;
  90 + try (var connection = connectionFactory.getConnection()) {
  91 + lock = redisLock.obtain(tbSecurityInfo.getEndpoint());
  92 + lock.lock();
  93 + if (info != null && info.getIdentity() != null) {
  94 + byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
  95 + if (oldEndpointBytes != null) {
  96 + String oldEndpoint = new String(oldEndpointBytes);
  97 + if (!oldEndpoint.equals(info.getEndpoint())) {
  98 + throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
  99 + }
  100 + connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
  101 + }
  102 + }
  103 +
  104 + byte[] previousData = connection.getSet((SEC_EP + tbSecurityInfo.getEndpoint()).getBytes(), tbSecurityInfoSerialized);
  105 + if (previousData != null && info != null) {
  106 + String previousIdentity = ((TbLwM2MSecurityInfo) serializer.asObject(previousData)).getSecurityInfo().getIdentity();
  107 + if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
  108 + connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
  109 + }
  110 + }
  111 + } finally {
  112 + if (lock != null) {
  113 + lock.unlock();
  114 + }
  115 + }
78 } 116 }
79 117
80 @Override 118 @Override
81 public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) { 119 public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
82 - //TODO: implement  
83 - return null; 120 + Lock lock = null;
  121 + try (var connection = connectionFactory.getConnection()) {
  122 + lock = redisLock.obtain(endpoint);
  123 + lock.lock();
  124 + byte[] data = connection.get((SEC_EP + endpoint).getBytes());
  125 + return (TbLwM2MSecurityInfo) serializer.asObject(data);
  126 + } finally {
  127 + if (lock != null) {
  128 + lock.unlock();
  129 + }
  130 + }
84 } 131 }
85 132
86 @Override 133 @Override
87 public void remove(String endpoint) { 134 public void remove(String endpoint) {
88 - //TODO: implement  
89 - }  
90 -  
91 - // @Override  
92 -// public Collection<SecurityInfo> getAll() {  
93 -// try (var connection = connectionFactory.getConnection()) {  
94 -// Collection<SecurityInfo> list = new LinkedList<>();  
95 -// ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();  
96 -// List<Cursor<byte[]>> scans = new ArrayList<>();  
97 -// if (connection instanceof RedisClusterConnection) {  
98 -// ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {  
99 -// scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));  
100 -// });  
101 -// } else {  
102 -// scans.add(connection.scan(scanOptions));  
103 -// }  
104 -//  
105 -// scans.forEach(scan -> {  
106 -// scan.forEachRemaining(key -> {  
107 -// byte[] element = connection.get(key);  
108 -// list.add(deserialize(element));  
109 -// });  
110 -// });  
111 -// return list;  
112 -// }  
113 -// }  
114 -//  
115 -// @Override  
116 -// public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {  
117 -// byte[] data = serialize(info);  
118 -// try (var connection = connectionFactory.getConnection()) {  
119 -// if (info.getIdentity() != null) {  
120 -// // populate the secondary index (security info by PSK id)  
121 -// String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));  
122 -// if (!oldEndpoint.equals(info.getEndpoint())) {  
123 -// throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");  
124 -// }  
125 -// connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());  
126 -// }  
127 -//  
128 -// byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);  
129 -// SecurityInfo previous = previousData == null ? null : deserialize(previousData);  
130 -// String previousIdentity = previous == null ? null : previous.getIdentity();  
131 -// if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {  
132 -// connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());  
133 -// }  
134 -//  
135 -// return previous;  
136 -// }  
137 -// }  
138 -//  
139 -// @Override  
140 -// public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {  
141 -// try (var connection = connectionFactory.getConnection()) {  
142 -// byte[] data = connection.get((SEC_EP + endpoint).getBytes());  
143 -//  
144 -// if (data != null) {  
145 -// SecurityInfo info = deserialize(data);  
146 -// if (info.getIdentity() != null) {  
147 -// connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());  
148 -// }  
149 -// connection.del((SEC_EP + endpoint).getBytes());  
150 -// if (listener != null) {  
151 -// listener.securityInfoRemoved(infosAreCompromised, info);  
152 -// }  
153 -// return info;  
154 -// }  
155 -// }  
156 -// return null;  
157 -// }  
158 -  
159 - private byte[] serialize(SecurityInfo secInfo) {  
160 - return SecurityInfoSerDes.serialize(secInfo); 135 + Lock lock = null;
  136 + try (var connection = connectionFactory.getConnection()) {
  137 + lock = redisLock.obtain(endpoint);
  138 + lock.lock();
  139 + byte[] data = connection.get((SEC_EP + endpoint).getBytes());
  140 + if (data != null) {
  141 + SecurityInfo info = ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo();
  142 + if (info != null && info.getIdentity() != null) {
  143 + connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
  144 + }
  145 + connection.del((SEC_EP + endpoint).getBytes());
  146 + }
  147 + } finally {
  148 + if (lock != null) {
  149 + lock.unlock();
  150 + }
  151 + }
161 } 152 }
162 153
163 - private SecurityInfo deserialize(byte[] data) {  
164 - return SecurityInfoSerDes.deserialize(data); 154 + private String toLockKey(String endpoint) {
  155 + return LOCK_EP + endpoint;
165 } 156 }
166 -  
167 } 157 }
@@ -21,8 +21,10 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -21,8 +21,10 @@ import org.thingsboard.server.common.data.id.DeviceId;
21 import org.thingsboard.server.common.data.id.DeviceProfileId; 21 import org.thingsboard.server.common.data.id.DeviceProfileId;
22 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
23 23
  24 +import java.io.Serializable;
  25 +
24 @Data 26 @Data
25 -public class TransportDeviceInfo { 27 +public class TransportDeviceInfo implements Serializable {
26 28
27 private TenantId tenantId; 29 private TenantId tenantId;
28 private CustomerId customerId; 30 private CustomerId customerId;
@@ -19,9 +19,11 @@ import lombok.Builder; @@ -19,9 +19,11 @@ import lombok.Builder;
19 import lombok.Data; 19 import lombok.Data;
20 import org.thingsboard.server.common.data.DeviceProfile; 20 import org.thingsboard.server.common.data.DeviceProfile;
21 21
  22 +import java.io.Serializable;
  23 +
22 @Data 24 @Data
23 @Builder 25 @Builder
24 -public class ValidateDeviceCredentialsResponse implements DeviceProfileAware { 26 +public class ValidateDeviceCredentialsResponse implements DeviceProfileAware, Serializable {
25 27
26 private final TransportDeviceInfo deviceInfo; 28 private final TransportDeviceInfo deviceInfo;
27 private final DeviceProfile deviceProfile; 29 private final DeviceProfile deviceProfile;