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 17
18 18 import com.fasterxml.jackson.core.type.TypeReference;
19 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 22 import org.eclipse.leshan.core.util.Hex;
  23 +import org.jetbrains.annotations.NotNull;
21 24 import org.junit.After;
22 25 import org.junit.Assert;
23 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 30 import org.thingsboard.common.util.JacksonUtil;
  31 +import org.thingsboard.server.common.data.Device;
25 32 import org.thingsboard.server.common.data.DeviceProfile;
26 33 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
27 34 import org.thingsboard.server.common.data.DeviceProfileType;
28 35 import org.thingsboard.server.common.data.DeviceTransportType;
  36 +import org.thingsboard.server.common.data.OtaPackageInfo;
29 37 import org.thingsboard.server.common.data.ResourceType;
30 38 import org.thingsboard.server.common.data.TbResource;
  39 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
31 40 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
32 41 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
33 42 import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
34 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 52 import org.thingsboard.server.controller.AbstractWebsocketTest;
36 53 import org.thingsboard.server.controller.TbTestWebSocketClient;
37 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 62 import java.io.IOException;
40 63 import java.io.InputStream;
... ... @@ -54,9 +77,15 @@ import java.security.spec.ECPrivateKeySpec;
54 77 import java.security.spec.ECPublicKeySpec;
55 78 import java.security.spec.KeySpec;
56 79 import java.util.Base64;
  80 +import java.util.Collections;
  81 +import java.util.List;
57 82 import java.util.concurrent.Executors;
58 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 89 @DaoSqlTest
61 90 public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
62 91
... ... @@ -139,6 +168,15 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
139 168 // certificates trustedby the server (should contain rootCA)
140 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 180 public AbstractLwM2MIntegrationTest() {
143 181 // create client credentials
144 182 try {
... ... @@ -262,10 +300,95 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
262 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 350 @After
266 351 public void after() {
267 352 executor.shutdownNow();
268 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 15 */
16 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 18 import org.junit.Assert;
21 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 20 import org.thingsboard.server.common.data.Device;
27 21 import org.thingsboard.server.common.data.OtaPackageInfo;
28 22 import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
... ... @@ -32,116 +26,30 @@ import org.thingsboard.server.common.data.query.EntityDataQuery;
32 26 import org.thingsboard.server.common.data.query.EntityKey;
33 27 import org.thingsboard.server.common.data.query.EntityKeyType;
34 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 29 import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
38 30 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
39 31 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
40 32 import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
41 33 import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
42   -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
43 34
44 35 import java.util.Collections;
45 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 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 40 @Test
104 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 47 @Test
141 48 public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception {
142 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 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 15 */
16 16 package org.thingsboard.server.transport.lwm2m;
17 17
18   -import org.eclipse.californium.core.network.config.NetworkConfig;
19 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 19 import org.junit.Test;
24   -import org.thingsboard.common.util.JacksonUtil;
25   -import org.thingsboard.server.common.data.Device;
26 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 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 23 import static org.eclipse.leshan.client.object.Security.x509;
47   -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
48 24
49 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 27 @Test
79 28 public void testConnectAndObserveTelemetry() throws Exception {
80   - createDeviceProfile(TRANSPORT_CONFIGURATION);
81 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 39 @Test
118 40 public void testConnectWithCertAndObserveTelemetry() throws Exception {
119   - createDeviceProfile(TRANSPORT_CONFIGURATION);
120 41 X509ClientCredentials credentials = new X509ClientCredentials();
121   - credentials.setEndpoint(endpoint);
  42 + credentials.setEndpoint(ENDPOINT);
122 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 15 */
16 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 21 import lombok.SneakyThrows;
19 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 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 20
21 21 @Getter
22 22 @Setter
23   -public class PSKClientCredentials extends HasKey {
  23 +public class PSKClientCredentials extends AbstractLwM2MClientCredentialsWithKey {
24 24 private String identity;
25 25
26 26 @Override
... ...
... ... @@ -15,7 +15,7 @@
15 15 */
16 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 20 @Override
21 21 public LwM2MSecurityMode getSecurityConfigClientMode() {
... ...
... ... @@ -21,10 +21,11 @@ import org.eclipse.leshan.core.request.BindingMode;
21 21 import org.eclipse.leshan.core.util.Hex;
22 22 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
23 23
  24 +import java.io.Serializable;
24 25 import java.nio.charset.StandardCharsets;
25 26
26 27 @Data
27   -public class LwM2MBootstrapConfig {
  28 +public class LwM2MBootstrapConfig implements Serializable {
28 29 /*
29 30 interface BootstrapSecurityConfig
30 31 servers: BootstrapServersSecurityConfig,
... ...
... ... @@ -138,10 +138,10 @@ public class LwM2mCredentialsSecurityInfoValidator {
138 138 PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
139 139 if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
140 140 try {
141   - if (pskConfig.getKey() != null && pskConfig.getKey().length > 0) {
  141 + if (pskConfig.getDecodedKey() != null && pskConfig.getDecodedKey().length > 0) {
142 142 endpoint = StringUtils.isNotEmpty(pskConfig.getEndpoint()) ? pskConfig.getEndpoint() : endpoint;
143 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 145 result.setSecurityMode(PSK);
146 146 }
147 147 }
... ... @@ -156,8 +156,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
156 156 private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
157 157 RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
158 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 161 result.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(endpoint, key));
162 162 result.setSecurityMode(RPK);
163 163 } else {
... ...
... ... @@ -23,8 +23,10 @@ import org.thingsboard.server.common.data.DeviceProfile;
23 23 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
24 24 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
25 25
  26 +import java.io.Serializable;
  27 +
26 28 @Data
27   -public class TbLwM2MSecurityInfo {
  29 +public class TbLwM2MSecurityInfo implements Serializable {
28 30 private ValidateDeviceCredentialsResponse msg;
29 31 private SecurityInfo securityInfo;
30 32 private SecurityMode securityMode;
... ...
... ... @@ -18,8 +18,10 @@ package org.thingsboard.server.transport.lwm2m.secure;
18 18 import lombok.Data;
19 19 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
20 20
  21 +import java.io.Serializable;
  22 +
21 23 @Data
22   -public class TbX509DtlsSessionInfo {
  24 +public class TbX509DtlsSessionInfo implements Serializable {
23 25
24 26 private final String x509CommonName;
25 27 private final ValidateDeviceCredentialsResponse credentials;
... ...
... ... @@ -15,28 +15,29 @@
15 15 */
16 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 19 import org.springframework.data.redis.connection.RedisConnectionFactory;
20   -import org.thingsboard.common.util.JacksonUtil;
21 20 import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
22 21
23 22 public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore {
24 23
25 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 28 public TbLwM2MDtlsSessionRedisStore(RedisConnectionFactory redisConnectionFactory) {
29 29 this.connectionFactory = redisConnectionFactory;
  30 + this.serializer = FSTConfiguration.createDefaultConfiguration();
30 31 }
31 32
32 33 @Override
33 34 public void put(String endpoint, TbX509DtlsSessionInfo msg) {
34 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 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 47 try (var c = connectionFactory.getConnection()) {
47 48 var data = c.get(getKey(endpoint));
48 49 if (data != null) {
49   - return JacksonUtil.fromString(new String(data), TbX509DtlsSessionInfo.class);
  50 + return (TbX509DtlsSessionInfo) serializer.asObject(data);
50 51 } else {
51 52 return null;
52 53 }
... ...
... ... @@ -15,49 +15,55 @@
15 15 */
16 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 18 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
21 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 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 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 27 public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
35 28 private static final String SEC_EP = "SEC#EP#";
36   -
  29 + private static final String LOCK_EP = "LOCK#EP#";
37 30 private static final String PSKID_SEC = "PSKID#SEC";
38 31
39 32 private final RedisConnectionFactory connectionFactory;
40   - private SecurityStoreListener listener;
  33 + private final FSTConfiguration serializer;
  34 + private final RedisLockRegistry redisLock;
41 35
42 36 public TbLwM2mRedisSecurityStore(RedisConnectionFactory connectionFactory) {
43 37 this.connectionFactory = connectionFactory;
  38 + redisLock = new RedisLockRegistry(connectionFactory, "Security");
  39 + serializer = FSTConfiguration.createDefaultConfiguration();
44 40 }
45 41
46 42 @Override
47 43 public SecurityInfo getByEndpoint(String endpoint) {
  44 + Lock lock = null;
48 45 try (var connection = connectionFactory.getConnection()) {
  46 + lock = redisLock.obtain(toLockKey(endpoint));
  47 + lock.lock();
49 48 byte[] data = connection.get((SEC_EP + endpoint).getBytes());
50 49 if (data == null) {
51 50 return null;
52 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 61 @Override
59 62 public SecurityInfo getByIdentity(String identity) {
  63 + Lock lock = null;
60 64 try (var connection = connectionFactory.getConnection()) {
  65 + lock = redisLock.obtain(toLockKey(identity));
  66 + lock.lock();
61 67 byte[] ep = connection.hGet(PSKID_SEC.getBytes(), identity.getBytes());
62 68 if (ep == null) {
63 69 return null;
... ... @@ -66,102 +72,86 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
66 72 if (data == null) {
67 73 return null;
68 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 85 @Override
76 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 118 @Override
81 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 133 @Override
87 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 21 import org.thingsboard.server.common.data.id.DeviceProfileId;
22 22 import org.thingsboard.server.common.data.id.TenantId;
23 23
  24 +import java.io.Serializable;
  25 +
24 26 @Data
25   -public class TransportDeviceInfo {
  27 +public class TransportDeviceInfo implements Serializable {
26 28
27 29 private TenantId tenantId;
28 30 private CustomerId customerId;
... ...
... ... @@ -19,9 +19,11 @@ import lombok.Builder;
19 19 import lombok.Data;
20 20 import org.thingsboard.server.common.data.DeviceProfile;
21 21
  22 +import java.io.Serializable;
  23 +
22 24 @Data
23 25 @Builder
24   -public class ValidateDeviceCredentialsResponse implements DeviceProfileAware {
  26 +public class ValidateDeviceCredentialsResponse implements DeviceProfileAware, Serializable {
25 27
26 28 private final TransportDeviceInfo deviceInfo;
27 29 private final DeviceProfile deviceProfile;
... ...