Commit d451768302ecfe8ee999288006c6af820478c7f1
Committed by
GitHub
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 |
application/src/test/java/org/thingsboard/server/transport/lwm2m/PskLwm2mIntegrationTest.java
0 → 100644
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 | +} |
application/src/test/java/org/thingsboard/server/transport/lwm2m/RpkLwM2MIntegrationTest.java
0 → 100644
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; |