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 | 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 | ... | ... |
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 | 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 | } | ... | ... |
... | ... | @@ -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; | ... | ... |