Commit 552a1efb233a8b79d16d71d69361854c80a1f648
Merge branch 'feature/lwm2m-certificate-verifier' of github.com:thingsboard/thin…
…gsboard into feature/lwm2m-certificate-verifier
Showing
16 changed files
with
413 additions
and
26 deletions
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m; | @@ -17,6 +17,7 @@ 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.leshan.core.util.Hex; | ||
20 | import org.junit.After; | 21 | import org.junit.After; |
21 | import org.junit.Assert; | 22 | import org.junit.Assert; |
22 | import org.junit.Before; | 23 | import org.junit.Before; |
@@ -35,6 +36,23 @@ import org.thingsboard.server.controller.AbstractWebsocketTest; | @@ -35,6 +36,23 @@ import org.thingsboard.server.controller.AbstractWebsocketTest; | ||
35 | import org.thingsboard.server.controller.TbTestWebSocketClient; | 36 | import org.thingsboard.server.controller.TbTestWebSocketClient; |
36 | import org.thingsboard.server.dao.service.DaoSqlTest; | 37 | import org.thingsboard.server.dao.service.DaoSqlTest; |
37 | 38 | ||
39 | +import java.io.IOException; | ||
40 | +import java.io.InputStream; | ||
41 | +import java.math.BigInteger; | ||
42 | +import java.security.AlgorithmParameters; | ||
43 | +import java.security.GeneralSecurityException; | ||
44 | +import java.security.KeyFactory; | ||
45 | +import java.security.KeyStore; | ||
46 | +import java.security.PrivateKey; | ||
47 | +import java.security.PublicKey; | ||
48 | +import java.security.cert.Certificate; | ||
49 | +import java.security.cert.X509Certificate; | ||
50 | +import java.security.spec.ECGenParameterSpec; | ||
51 | +import java.security.spec.ECParameterSpec; | ||
52 | +import java.security.spec.ECPoint; | ||
53 | +import java.security.spec.ECPrivateKeySpec; | ||
54 | +import java.security.spec.ECPublicKeySpec; | ||
55 | +import java.security.spec.KeySpec; | ||
38 | import java.util.Base64; | 56 | import java.util.Base64; |
39 | import java.util.concurrent.Executors; | 57 | import java.util.concurrent.Executors; |
40 | import java.util.concurrent.ScheduledExecutorService; | 58 | import java.util.concurrent.ScheduledExecutorService; |
@@ -46,6 +64,114 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { | @@ -46,6 +64,114 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { | ||
46 | protected ScheduledExecutorService executor; | 64 | protected ScheduledExecutorService executor; |
47 | protected TbTestWebSocketClient wsClient; | 65 | protected TbTestWebSocketClient wsClient; |
48 | 66 | ||
67 | + protected final PublicKey clientPublicKey; // client public key used for RPK | ||
68 | + protected final PrivateKey clientPrivateKey; // client private key used for RPK | ||
69 | + protected final PublicKey serverPublicKey; // server public key used for RPK | ||
70 | + protected final PrivateKey serverPrivateKey; // server private key used for RPK | ||
71 | + | ||
72 | + // client private key used for X509 | ||
73 | + protected final PrivateKey clientPrivateKeyFromCert; | ||
74 | + // server private key used for X509 | ||
75 | + protected final PrivateKey serverPrivateKeyFromCert; | ||
76 | + // client certificate signed by rootCA with a good CN (CN start by leshan_integration_test) | ||
77 | + protected final X509Certificate clientX509Cert; | ||
78 | + // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test) | ||
79 | + protected final X509Certificate clientX509CertWithBadCN; | ||
80 | + // client certificate self-signed with a good CN (CN start by leshan_integration_test) | ||
81 | + protected final X509Certificate clientX509CertSelfSigned; | ||
82 | + // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test) | ||
83 | + protected final X509Certificate clientX509CertNotTrusted; | ||
84 | + // server certificate signed by rootCA | ||
85 | + protected final X509Certificate serverX509Cert; | ||
86 | + // self-signed server certificate | ||
87 | + protected final X509Certificate serverX509CertSelfSigned; | ||
88 | + // rootCA used by the server | ||
89 | + protected final X509Certificate rootCAX509Cert; | ||
90 | + // certificates trustedby the server (should contain rootCA) | ||
91 | + protected final Certificate[] trustedCertificates = new Certificate[1]; | ||
92 | + | ||
93 | + public AbstractLwM2MIntegrationTest() { | ||
94 | +// create client credentials | ||
95 | + try { | ||
96 | + // Get point values | ||
97 | + byte[] publicX = Hex | ||
98 | + .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); | ||
99 | + byte[] publicY = Hex | ||
100 | + .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); | ||
101 | + byte[] privateS = Hex | ||
102 | + .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); | ||
103 | + | ||
104 | + // Get Elliptic Curve Parameter spec for secp256r1 | ||
105 | + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); | ||
106 | + algoParameters.init(new ECGenParameterSpec("secp256r1")); | ||
107 | + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); | ||
108 | + | ||
109 | + // Create key specs | ||
110 | + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), | ||
111 | + parameterSpec); | ||
112 | + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); | ||
113 | + | ||
114 | + // Get keys | ||
115 | + clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); | ||
116 | + clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); | ||
117 | + | ||
118 | + // Get certificates from key store | ||
119 | + char[] clientKeyStorePwd = "client".toCharArray(); | ||
120 | + KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | ||
121 | + try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) { | ||
122 | + clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); | ||
123 | + } | ||
124 | + | ||
125 | + clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd); | ||
126 | + clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client"); | ||
127 | + clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn"); | ||
128 | + clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed"); | ||
129 | + clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted"); | ||
130 | + } catch (GeneralSecurityException | IOException e) { | ||
131 | + throw new RuntimeException(e); | ||
132 | + } | ||
133 | + | ||
134 | + // create server credentials | ||
135 | + try { | ||
136 | + // Get point values | ||
137 | + byte[] publicX = Hex | ||
138 | + .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); | ||
139 | + byte[] publicY = Hex | ||
140 | + .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); | ||
141 | + byte[] privateS = Hex | ||
142 | + .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); | ||
143 | + | ||
144 | + // Get Elliptic Curve Parameter spec for secp256r1 | ||
145 | + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); | ||
146 | + algoParameters.init(new ECGenParameterSpec("secp256r1")); | ||
147 | + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); | ||
148 | + | ||
149 | + // Create key specs | ||
150 | + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), | ||
151 | + parameterSpec); | ||
152 | + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); | ||
153 | + | ||
154 | +// // Get keys | ||
155 | + serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); | ||
156 | + serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); | ||
157 | + | ||
158 | + // Get certificates from key store | ||
159 | + char[] serverKeyStorePwd = "server".toCharArray(); | ||
160 | + KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | ||
161 | + try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) { | ||
162 | + serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd); | ||
163 | + } | ||
164 | + | ||
165 | + serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); | ||
166 | + rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA"); | ||
167 | + serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server"); | ||
168 | + serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed"); | ||
169 | + trustedCertificates[0] = rootCAX509Cert; | ||
170 | + } catch (GeneralSecurityException | IOException e) { | ||
171 | + throw new RuntimeException(e); | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
49 | @Before | 175 | @Before |
50 | public void beforeTest() throws Exception { | 176 | public void beforeTest() throws Exception { |
51 | executor = Executors.newScheduledThreadPool(10); | 177 | executor = Executors.newScheduledThreadPool(10); |
@@ -60,7 +186,8 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { | @@ -60,7 +186,8 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { | ||
60 | lwModel.setTenantId(tenantId); | 186 | lwModel.setTenantId(tenantId); |
61 | byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); | 187 | byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); |
62 | lwModel.setData(Base64.getEncoder().encodeToString(bytes)); | 188 | lwModel.setData(Base64.getEncoder().encodeToString(bytes)); |
63 | - lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>(){}); | 189 | + lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() { |
190 | + }); | ||
64 | Assert.assertNotNull(lwModel); | 191 | Assert.assertNotNull(lwModel); |
65 | } | 192 | } |
66 | wsClient = buildAndConnectWebSocketClient(); | 193 | wsClient = buildAndConnectWebSocketClient(); |
@@ -69,7 +196,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { | @@ -69,7 +196,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { | ||
69 | protected void createDeviceProfile(String transportConfiguration) throws Exception { | 196 | protected void createDeviceProfile(String transportConfiguration) throws Exception { |
70 | deviceProfile = new DeviceProfile(); | 197 | deviceProfile = new DeviceProfile(); |
71 | 198 | ||
72 | - deviceProfile.setName("LwM2M No Security"); | 199 | + deviceProfile.setName("LwM2M"); |
73 | deviceProfile.setType(DeviceProfileType.DEFAULT); | 200 | deviceProfile.setType(DeviceProfileType.DEFAULT); |
74 | deviceProfile.setTenantId(tenantId); | 201 | deviceProfile.setTenantId(tenantId); |
75 | deviceProfile.setTransportType(DeviceTransportType.LWM2M); | 202 | deviceProfile.setTransportType(DeviceTransportType.LWM2M); |
@@ -15,6 +15,8 @@ | @@ -15,6 +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; | ||
18 | import org.jetbrains.annotations.NotNull; | 20 | import org.jetbrains.annotations.NotNull; |
19 | import org.junit.Assert; | 21 | import org.junit.Assert; |
20 | import org.junit.Test; | 22 | import org.junit.Test; |
@@ -39,6 +41,7 @@ import org.thingsboard.server.transport.lwm2m.secure.credentials.NoSecClientCred | @@ -39,6 +41,7 @@ import org.thingsboard.server.transport.lwm2m.secure.credentials.NoSecClientCred | ||
39 | import java.util.Collections; | 41 | import java.util.Collections; |
40 | import java.util.List; | 42 | import java.util.List; |
41 | 43 | ||
44 | +import static org.eclipse.leshan.client.object.Security.noSec; | ||
42 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | 45 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
43 | 46 | ||
44 | public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { | 47 | public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
@@ -91,6 +94,10 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { | @@ -91,6 +94,10 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { | ||
91 | " }\n" + | 94 | " }\n" + |
92 | "}"; | 95 | "}"; |
93 | 96 | ||
97 | + private final int port = 5685; | ||
98 | + private final Security security = noSec("coap://localhost:" + port, 123); | ||
99 | + private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port)); | ||
100 | + | ||
94 | @NotNull | 101 | @NotNull |
95 | private Device createDevice(String deviceAEndpoint) throws Exception { | 102 | private Device createDevice(String deviceAEndpoint) throws Exception { |
96 | Device device = new Device(); | 103 | Device device = new Device(); |
@@ -138,7 +145,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { | @@ -138,7 +145,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { | ||
138 | 145 | ||
139 | wsClient.registerWaitForUpdate(); | 146 | wsClient.registerWaitForUpdate(); |
140 | LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint); | 147 | LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint); |
141 | - client.init(); | 148 | + client.init(security, coapConfig); |
142 | String msg = wsClient.waitForUpdate(); | 149 | String msg = wsClient.waitForUpdate(); |
143 | 150 | ||
144 | EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); | 151 | EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); |
application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.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.californium.core.network.config.NetworkConfig; | ||
19 | +import org.eclipse.leshan.client.object.Security; | ||
20 | +import org.jetbrains.annotations.NotNull; | ||
21 | +import org.junit.Assert; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.common.util.JacksonUtil; | ||
24 | +import org.thingsboard.server.common.data.Device; | ||
25 | +import org.thingsboard.server.common.data.query.EntityData; | ||
26 | +import org.thingsboard.server.common.data.query.EntityDataPageLink; | ||
27 | +import org.thingsboard.server.common.data.query.EntityDataQuery; | ||
28 | +import org.thingsboard.server.common.data.query.EntityKey; | ||
29 | +import org.thingsboard.server.common.data.query.EntityKeyType; | ||
30 | +import org.thingsboard.server.common.data.query.SingleEntityFilter; | ||
31 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
32 | +import org.thingsboard.server.common.data.security.DeviceCredentialsType; | ||
33 | +import org.thingsboard.server.common.transport.util.SslUtil; | ||
34 | +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; | ||
35 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; | ||
36 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; | ||
37 | +import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; | ||
38 | +import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; | ||
39 | +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; | ||
40 | +import org.thingsboard.server.transport.lwm2m.secure.credentials.X509ClientCredentialsConfig; | ||
41 | + | ||
42 | +import java.util.Collections; | ||
43 | +import java.util.List; | ||
44 | + | ||
45 | +import static org.eclipse.leshan.client.object.Security.x509; | ||
46 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
47 | + | ||
48 | +public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { | ||
49 | + | ||
50 | + protected final String TRANSPORT_CONFIGURATION = "{\n" + | ||
51 | + " \"type\": \"LWM2M\",\n" + | ||
52 | + " \"observeAttr\": {\n" + | ||
53 | + " \"keyName\": {\n" + | ||
54 | + " \"/3_1.0/0/9\": \"batteryLevel\"\n" + | ||
55 | + " },\n" + | ||
56 | + " \"observe\": [],\n" + | ||
57 | + " \"attribute\": [\n" + | ||
58 | + " ],\n" + | ||
59 | + " \"telemetry\": [\n" + | ||
60 | + " \"/3_1.0/0/9\"\n" + | ||
61 | + " ],\n" + | ||
62 | + " \"attributeLwm2m\": {}\n" + | ||
63 | + " },\n" + | ||
64 | + " \"bootstrap\": {\n" + | ||
65 | + " \"servers\": {\n" + | ||
66 | + " \"binding\": \"UQ\",\n" + | ||
67 | + " \"shortId\": 123,\n" + | ||
68 | + " \"lifetime\": 300,\n" + | ||
69 | + " \"notifIfDisabled\": true,\n" + | ||
70 | + " \"defaultMinPeriod\": 1\n" + | ||
71 | + " },\n" + | ||
72 | + " \"lwm2mServer\": {\n" + | ||
73 | + " \"host\": \"localhost\",\n" + | ||
74 | + " \"port\": 5686,\n" + | ||
75 | + " \"serverId\": 123,\n" + | ||
76 | + " \"serverPublicKey\": \"\",\n" + | ||
77 | + " \"bootstrapServerIs\": false,\n" + | ||
78 | + " \"clientHoldOffTime\": 1,\n" + | ||
79 | + " \"bootstrapServerAccountTimeout\": 0\n" + | ||
80 | + " },\n" + | ||
81 | + " \"bootstrapServer\": {\n" + | ||
82 | + " \"host\": \"localhost\",\n" + | ||
83 | + " \"port\": 5687,\n" + | ||
84 | + " \"serverId\": 111,\n" + | ||
85 | + " \"securityMode\": \"NO_SEC\",\n" + | ||
86 | + " \"serverPublicKey\": \"\",\n" + | ||
87 | + " \"bootstrapServerIs\": true,\n" + | ||
88 | + " \"clientHoldOffTime\": 1,\n" + | ||
89 | + " \"bootstrapServerAccountTimeout\": 0\n" + | ||
90 | + " }\n" + | ||
91 | + " },\n" + | ||
92 | + " \"clientLwM2mSettings\": {\n" + | ||
93 | + " \"clientOnlyObserveAfterConnect\": 1\n" + | ||
94 | + " }\n" + | ||
95 | + "}"; | ||
96 | + | ||
97 | + | ||
98 | + private final int port = 5686; | ||
99 | + private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port)); | ||
100 | + private final String endpoint = "deviceAEndpoint"; | ||
101 | + private final String serverUri = "coaps://localhost:" + port; | ||
102 | + | ||
103 | + @NotNull | ||
104 | + private Device createDevice(String credentialsId, X509ClientCredentialsConfig credentialsConfig) throws Exception { | ||
105 | + Device device = new Device(); | ||
106 | + device.setName("Device A"); | ||
107 | + device.setDeviceProfileId(deviceProfile.getId()); | ||
108 | + device.setTenantId(tenantId); | ||
109 | + device = doPost("/api/device", device, Device.class); | ||
110 | + Assert.assertNotNull(device); | ||
111 | + | ||
112 | + DeviceCredentials deviceCredentials = | ||
113 | + doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
114 | + Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); | ||
115 | + deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); | ||
116 | + | ||
117 | + deviceCredentials.setCredentialsId(credentialsId); | ||
118 | + | ||
119 | + LwM2MCredentials X509Credentials = new LwM2MCredentials(); | ||
120 | + | ||
121 | + X509Credentials.setClient(credentialsConfig); | ||
122 | + | ||
123 | + deviceCredentials.setCredentialsValue(JacksonUtil.toString(X509Credentials)); | ||
124 | + doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); | ||
125 | + return device; | ||
126 | + } | ||
127 | + | ||
128 | + @Test | ||
129 | + public void testConnectAndObserveTelemetry() throws Exception { | ||
130 | + createDeviceProfile(TRANSPORT_CONFIGURATION); | ||
131 | + | ||
132 | + Device device = createDevice(endpoint, new X509ClientCredentialsConfig(null, null)); | ||
133 | + | ||
134 | + SingleEntityFilter sef = new SingleEntityFilter(); | ||
135 | + sef.setSingleEntity(device.getId()); | ||
136 | + LatestValueCmd latestCmd = new LatestValueCmd(); | ||
137 | + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); | ||
138 | + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), | ||
139 | + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); | ||
140 | + | ||
141 | + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); | ||
142 | + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); | ||
143 | + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); | ||
144 | + | ||
145 | + wsClient.send(mapper.writeValueAsString(wrapper)); | ||
146 | + wsClient.waitForReply(); | ||
147 | + | ||
148 | + wsClient.registerWaitForUpdate(); | ||
149 | + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); | ||
150 | + Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); | ||
151 | + client.init(security, coapConfig); | ||
152 | + String msg = wsClient.waitForUpdate(); | ||
153 | + | ||
154 | + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); | ||
155 | + Assert.assertEquals(1, update.getCmdId()); | ||
156 | + List<EntityData> eData = update.getUpdate(); | ||
157 | + Assert.assertNotNull(eData); | ||
158 | + Assert.assertEquals(1, eData.size()); | ||
159 | + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); | ||
160 | + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); | ||
161 | + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); | ||
162 | + Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); | ||
163 | + client.destroy(); | ||
164 | + } | ||
165 | + | ||
166 | + @Test | ||
167 | + public void testConnectWithCertAndObserveTelemetry() throws Exception { | ||
168 | + createDeviceProfile(TRANSPORT_CONFIGURATION); | ||
169 | + Device device = createDevice(null, new X509ClientCredentialsConfig(SslUtil.getCertificateString(clientX509CertNotTrusted), endpoint)); | ||
170 | + | ||
171 | + SingleEntityFilter sef = new SingleEntityFilter(); | ||
172 | + sef.setSingleEntity(device.getId()); | ||
173 | + LatestValueCmd latestCmd = new LatestValueCmd(); | ||
174 | + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); | ||
175 | + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), | ||
176 | + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); | ||
177 | + | ||
178 | + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); | ||
179 | + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); | ||
180 | + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); | ||
181 | + | ||
182 | + wsClient.send(mapper.writeValueAsString(wrapper)); | ||
183 | + wsClient.waitForReply(); | ||
184 | + | ||
185 | + wsClient.registerWaitForUpdate(); | ||
186 | + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); | ||
187 | + | ||
188 | + Security security = x509(serverUri, 123, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); | ||
189 | + | ||
190 | + client.init(security, coapConfig); | ||
191 | + String msg = wsClient.waitForUpdate(); | ||
192 | + | ||
193 | + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); | ||
194 | + Assert.assertEquals(1, update.getCmdId()); | ||
195 | + List<EntityData> eData = update.getUpdate(); | ||
196 | + Assert.assertNotNull(eData); | ||
197 | + Assert.assertEquals(1, eData.size()); | ||
198 | + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); | ||
199 | + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); | ||
200 | + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); | ||
201 | + Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); | ||
202 | + client.destroy(); | ||
203 | + } | ||
204 | + | ||
205 | +} |
@@ -32,6 +32,7 @@ import org.eclipse.californium.scandium.dtls.SessionAdapter; | @@ -32,6 +32,7 @@ import org.eclipse.californium.scandium.dtls.SessionAdapter; | ||
32 | import org.eclipse.leshan.client.californium.LeshanClient; | 32 | import org.eclipse.leshan.client.californium.LeshanClient; |
33 | import org.eclipse.leshan.client.californium.LeshanClientBuilder; | 33 | import org.eclipse.leshan.client.californium.LeshanClientBuilder; |
34 | import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; | 34 | import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; |
35 | +import org.eclipse.leshan.client.object.Security; | ||
35 | import org.eclipse.leshan.client.object.Server; | 36 | import org.eclipse.leshan.client.object.Server; |
36 | import org.eclipse.leshan.client.observer.LwM2mClientObserver; | 37 | import org.eclipse.leshan.client.observer.LwM2mClientObserver; |
37 | import org.eclipse.leshan.client.resource.ObjectsInitializer; | 38 | import org.eclipse.leshan.client.resource.ObjectsInitializer; |
@@ -54,7 +55,6 @@ import java.util.ArrayList; | @@ -54,7 +55,6 @@ import java.util.ArrayList; | ||
54 | import java.util.List; | 55 | import java.util.List; |
55 | import java.util.concurrent.ScheduledExecutorService; | 56 | import java.util.concurrent.ScheduledExecutorService; |
56 | 57 | ||
57 | -import static org.eclipse.leshan.client.object.Security.noSec; | ||
58 | import static org.eclipse.leshan.core.LwM2mId.DEVICE; | 58 | import static org.eclipse.leshan.core.LwM2mId.DEVICE; |
59 | import static org.eclipse.leshan.core.LwM2mId.SECURITY; | 59 | import static org.eclipse.leshan.core.LwM2mId.SECURITY; |
60 | import static org.eclipse.leshan.core.LwM2mId.SERVER; | 60 | import static org.eclipse.leshan.core.LwM2mId.SERVER; |
@@ -67,7 +67,7 @@ public class LwM2MTestClient { | @@ -67,7 +67,7 @@ public class LwM2MTestClient { | ||
67 | private final String endpoint; | 67 | private final String endpoint; |
68 | private LeshanClient client; | 68 | private LeshanClient client; |
69 | 69 | ||
70 | - public void init() { | 70 | + public void init(Security security, NetworkConfig coapConfig) { |
71 | String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"}; | 71 | String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"}; |
72 | List<ObjectModel> models = new ArrayList<>(); | 72 | List<ObjectModel> models = new ArrayList<>(); |
73 | for (String resourceName : resources) { | 73 | for (String resourceName : resources) { |
@@ -75,13 +75,10 @@ public class LwM2MTestClient { | @@ -75,13 +75,10 @@ public class LwM2MTestClient { | ||
75 | } | 75 | } |
76 | LwM2mModel model = new StaticModel(models); | 76 | LwM2mModel model = new StaticModel(models); |
77 | ObjectsInitializer initializer = new ObjectsInitializer(model); | 77 | ObjectsInitializer initializer = new ObjectsInitializer(model); |
78 | - initializer.setInstancesForObject(SECURITY, noSec("coap://localhost:5685", 123)); | 78 | + initializer.setInstancesForObject(SECURITY, security); |
79 | initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false)); | 79 | initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false)); |
80 | initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice()); | 80 | initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice()); |
81 | 81 | ||
82 | - NetworkConfig coapConfig = new NetworkConfig(); | ||
83 | - coapConfig.setString("COAP_PORT", Integer.toString(5685)); | ||
84 | - | ||
85 | DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); | 82 | DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); |
86 | dtlsConfig.setRecommendedCipherSuitesOnly(true); | 83 | dtlsConfig.setRecommendedCipherSuitesOnly(true); |
87 | 84 | ||
@@ -256,7 +253,7 @@ public class LwM2MTestClient { | @@ -256,7 +253,7 @@ public class LwM2MTestClient { | ||
256 | } | 253 | } |
257 | 254 | ||
258 | public void destroy() { | 255 | public void destroy() { |
259 | - client.stop(false); | 256 | + client.destroy(true); |
260 | } | 257 | } |
261 | 258 | ||
262 | } | 259 | } |
No preview for this file type
No preview for this file type
@@ -43,7 +43,7 @@ public class TbLwM2MAuthorizer implements Authorizer { | @@ -43,7 +43,7 @@ public class TbLwM2MAuthorizer implements Authorizer { | ||
43 | if (senderIdentity.isX509()) { | 43 | if (senderIdentity.isX509()) { |
44 | TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint()); | 44 | TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint()); |
45 | if (sessionInfo != null) { | 45 | if (sessionInfo != null) { |
46 | - if (senderIdentity.getX509CommonName().equals(sessionInfo.getX509CommonName())) { | 46 | + if (sessionInfo.getX509CommonName().endsWith(senderIdentity.getX509CommonName())) { |
47 | clientContext.registerClient(registration, sessionInfo.getCredentials()); | 47 | clientContext.registerClient(registration, sessionInfo.getCredentials()); |
48 | // X509 certificate is valid and matches endpoint. | 48 | // X509 certificate is valid and matches endpoint. |
49 | return registration; | 49 | return registration; |
@@ -15,14 +15,17 @@ | @@ -15,14 +15,17 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.lwm2m.secure.credentials; | 16 | package org.thingsboard.server.transport.lwm2m.secure.credentials; |
17 | 17 | ||
18 | +import lombok.AllArgsConstructor; | ||
18 | import lombok.Data; | 19 | import lombok.Data; |
20 | +import lombok.NoArgsConstructor; | ||
19 | import org.eclipse.leshan.core.SecurityMode; | 21 | import org.eclipse.leshan.core.SecurityMode; |
20 | 22 | ||
21 | import static org.eclipse.leshan.core.SecurityMode.X509; | 23 | import static org.eclipse.leshan.core.SecurityMode.X509; |
22 | 24 | ||
23 | @Data | 25 | @Data |
26 | +@NoArgsConstructor | ||
27 | +@AllArgsConstructor | ||
24 | public class X509ClientCredentialsConfig implements LwM2MClientCredentialsConfig { | 28 | public class X509ClientCredentialsConfig implements LwM2MClientCredentialsConfig { |
25 | - private boolean allowTrustedOnly; | ||
26 | private String cert; | 29 | private String cert; |
27 | private String endpoint; | 30 | private String endpoint; |
28 | 31 |
@@ -63,6 +63,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; | @@ -63,6 +63,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; | ||
63 | import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; | 63 | import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; |
64 | import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; | 64 | import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; |
65 | import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; | 65 | import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; |
66 | +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; | ||
66 | import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | 67 | import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; |
67 | 68 | ||
68 | import javax.annotation.PostConstruct; | 69 | import javax.annotation.PostConstruct; |
@@ -129,12 +130,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler | @@ -129,12 +130,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler | ||
129 | private final LwM2MJsonAdaptor adaptor; | 130 | private final LwM2MJsonAdaptor adaptor; |
130 | private final LwM2mClientContext clientContext; | 131 | private final LwM2mClientContext clientContext; |
131 | private final LwM2mTransportRequest lwM2mTransportRequest; | 132 | private final LwM2mTransportRequest lwM2mTransportRequest; |
133 | + private final TbLwM2MDtlsSessionStore sessionStore; | ||
132 | 134 | ||
133 | public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, | 135 | public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, |
134 | LwM2mClientContext clientContext, | 136 | LwM2mClientContext clientContext, |
135 | @Lazy LwM2mTransportRequest lwM2mTransportRequest, | 137 | @Lazy LwM2mTransportRequest lwM2mTransportRequest, |
136 | FirmwareDataCache firmwareDataCache, | 138 | FirmwareDataCache firmwareDataCache, |
137 | - LwM2mTransportContext context, LwM2MJsonAdaptor adaptor) { | 139 | + LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { |
138 | this.transportService = transportService; | 140 | this.transportService = transportService; |
139 | this.config = config; | 141 | this.config = config; |
140 | this.helper = helper; | 142 | this.helper = helper; |
@@ -143,6 +145,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler | @@ -143,6 +145,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler | ||
143 | this.firmwareDataCache = firmwareDataCache; | 145 | this.firmwareDataCache = firmwareDataCache; |
144 | this.context = context; | 146 | this.context = context; |
145 | this.adaptor = adaptor; | 147 | this.adaptor = adaptor; |
148 | + this.sessionStore = sessionStore; | ||
146 | } | 149 | } |
147 | 150 | ||
148 | @PostConstruct | 151 | @PostConstruct |
@@ -243,6 +246,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler | @@ -243,6 +246,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler | ||
243 | SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); | 246 | SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); |
244 | if (sessionInfo != null) { | 247 | if (sessionInfo != null) { |
245 | transportService.deregisterSession(sessionInfo); | 248 | transportService.deregisterSession(sessionInfo); |
249 | + sessionStore.remove(registration.getEndpoint()); | ||
246 | this.doCloseSession(sessionInfo); | 250 | this.doCloseSession(sessionInfo); |
247 | clientContext.removeClientByRegistrationId(registration.getId()); | 251 | clientContext.removeClientByRegistrationId(registration.getId()); |
248 | log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); | 252 | log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); |
@@ -141,6 +141,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { | @@ -141,6 +141,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { | ||
141 | LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); | 141 | LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); |
142 | lwM2mClientsByEndpoint.put(registration.getEndpoint(), client); | 142 | lwM2mClientsByEndpoint.put(registration.getEndpoint(), client); |
143 | lwM2mClientsByRegistrationId.put(registration.getId(), client); | 143 | lwM2mClientsByRegistrationId.put(registration.getId(), client); |
144 | + toClientProfile(credentials.getDeviceProfile()); | ||
144 | } | 145 | } |
145 | 146 | ||
146 | @Override | 147 | @Override |
@@ -36,4 +36,9 @@ public class TbL2M2MDtlsSessionInMemoryStore implements TbLwM2MDtlsSessionStore | @@ -36,4 +36,9 @@ public class TbL2M2MDtlsSessionInMemoryStore implements TbLwM2MDtlsSessionStore | ||
36 | public TbX509DtlsSessionInfo get(String endpoint) { | 36 | public TbX509DtlsSessionInfo get(String endpoint) { |
37 | return store.get(endpoint); | 37 | return store.get(endpoint); |
38 | } | 38 | } |
39 | + | ||
40 | + @Override | ||
41 | + public void remove(String endpoint) { | ||
42 | + store.remove(endpoint); | ||
43 | + } | ||
39 | } | 44 | } |
@@ -24,6 +24,9 @@ public interface TbLwM2MDtlsSessionStore { | @@ -24,6 +24,9 @@ public interface TbLwM2MDtlsSessionStore { | ||
24 | 24 | ||
25 | TbX509DtlsSessionInfo get(String endpoint); | 25 | TbX509DtlsSessionInfo get(String endpoint); |
26 | 26 | ||
27 | + | ||
28 | + void remove(String endpoint); | ||
29 | + | ||
27 | //TODO: add way to delete the session by endpoint. | 30 | //TODO: add way to delete the session by endpoint. |
28 | 31 | ||
29 | } | 32 | } |
@@ -226,19 +226,29 @@ public class JsonConverter { | @@ -226,19 +226,29 @@ public class JsonConverter { | ||
226 | } | 226 | } |
227 | 227 | ||
228 | private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) { | 228 | private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) { |
229 | - if (value.getAsString().contains(".")) { | ||
230 | - return KeyValueProto.newBuilder() | ||
231 | - .setKey(key) | ||
232 | - .setType(KeyValueType.DOUBLE_V) | ||
233 | - .setDoubleV(value.getAsDouble()) | ||
234 | - .build(); | 229 | + String valueAsString = value.getAsString(); |
230 | + KeyValueProto.Builder builder = KeyValueProto.newBuilder().setKey(key); | ||
231 | + if (valueAsString.contains("e") || valueAsString.contains("E")) { | ||
232 | + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String | ||
233 | + var bd = new BigDecimal(valueAsString); | ||
234 | + if (bd.stripTrailingZeros().scale() <= 0) { | ||
235 | + try { | ||
236 | + return builder.setType(KeyValueType.LONG_V).setLongV(bd.longValueExact()).build(); | ||
237 | + } catch (ArithmeticException e) { | ||
238 | + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(bd.doubleValue()).build(); | ||
239 | + } | ||
240 | + } else { | ||
241 | + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(bd.doubleValue()).build(); | ||
242 | + } | ||
243 | + } else if (valueAsString.contains(".")) { | ||
244 | + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(value.getAsDouble()).build(); | ||
235 | } else { | 245 | } else { |
236 | try { | 246 | try { |
237 | long longValue = Long.parseLong(value.getAsString()); | 247 | long longValue = Long.parseLong(value.getAsString()); |
238 | - return KeyValueProto.newBuilder().setKey(key).setType(KeyValueType.LONG_V) | ||
239 | - .setLongV(longValue).build(); | 248 | + return builder.setType(KeyValueType.LONG_V).setLongV(longValue).build(); |
240 | } catch (NumberFormatException e) { | 249 | } catch (NumberFormatException e) { |
241 | - throw new JsonSyntaxException("Big integer values are not supported!"); | 250 | + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String |
251 | + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(new BigDecimal(valueAsString).doubleValue()).build(); | ||
242 | } | 252 | } |
243 | } | 253 | } |
244 | } | 254 | } |
@@ -252,6 +262,7 @@ public class JsonConverter { | @@ -252,6 +262,7 @@ public class JsonConverter { | ||
252 | String valueAsString = value.getAsString(); | 262 | String valueAsString = value.getAsString(); |
253 | String key = valueEntry.getKey(); | 263 | String key = valueEntry.getKey(); |
254 | if (valueAsString.contains("e") || valueAsString.contains("E")) { | 264 | if (valueAsString.contains("e") || valueAsString.contains("E")) { |
265 | + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String | ||
255 | var bd = new BigDecimal(valueAsString); | 266 | var bd = new BigDecimal(valueAsString); |
256 | if (bd.stripTrailingZeros().scale() <= 0) { | 267 | if (bd.stripTrailingZeros().scale() <= 0) { |
257 | try { | 268 | try { |
@@ -269,7 +280,8 @@ public class JsonConverter { | @@ -269,7 +280,8 @@ public class JsonConverter { | ||
269 | long longValue = Long.parseLong(value.getAsString()); | 280 | long longValue = Long.parseLong(value.getAsString()); |
270 | result.add(new LongDataEntry(key, longValue)); | 281 | result.add(new LongDataEntry(key, longValue)); |
271 | } catch (NumberFormatException e) { | 282 | } catch (NumberFormatException e) { |
272 | - throw new JsonSyntaxException("Big integer values are not supported!"); | 283 | + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String |
284 | + result.add(new DoubleDataEntry(key, new BigDecimal(valueAsString).doubleValue())); | ||
273 | } | 285 | } |
274 | } | 286 | } |
275 | } | 287 | } |
@@ -21,6 +21,8 @@ import org.junit.runner.RunWith; | @@ -21,6 +21,8 @@ import org.junit.runner.RunWith; | ||
21 | import org.mockito.junit.MockitoJUnitRunner; | 21 | import org.mockito.junit.MockitoJUnitRunner; |
22 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 22 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
23 | 23 | ||
24 | +import java.util.ArrayList; | ||
25 | + | ||
24 | @RunWith(MockitoJUnitRunner.class) | 26 | @RunWith(MockitoJUnitRunner.class) |
25 | public class JsonConverterTest { | 27 | public class JsonConverterTest { |
26 | 28 | ||
@@ -39,6 +41,12 @@ public class JsonConverterTest { | @@ -39,6 +41,12 @@ public class JsonConverterTest { | ||
39 | } | 41 | } |
40 | 42 | ||
41 | @Test | 43 | @Test |
44 | + public void testParseAttributesBigDecimalAsLong() { | ||
45 | + var result = new ArrayList<>(JsonConverter.convertToAttributes(JSON_PARSER.parse("{\"meterReadingDelta\": 1E1}"))); | ||
46 | + Assert.assertEquals(10L, result.get(0).getLongValue().get().longValue()); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
42 | public void testParseAsDouble() { | 50 | public void testParseAsDouble() { |
43 | var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1.1}"), 0L); | 51 | var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1.1}"), 0L); |
44 | Assert.assertEquals(1.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); | 52 | Assert.assertEquals(1.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); |
@@ -16,6 +16,8 @@ | @@ -16,6 +16,8 @@ | ||
16 | package org.thingsboard.server.dao.device; | 16 | package org.thingsboard.server.dao.device; |
17 | 17 | ||
18 | 18 | ||
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
19 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
20 | import org.hibernate.exception.ConstraintViolationException; | 22 | import org.hibernate.exception.ConstraintViolationException; |
21 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
@@ -23,6 +25,7 @@ import org.springframework.cache.annotation.CacheEvict; | @@ -23,6 +25,7 @@ import org.springframework.cache.annotation.CacheEvict; | ||
23 | import org.springframework.cache.annotation.Cacheable; | 25 | import org.springframework.cache.annotation.Cacheable; |
24 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
25 | import org.springframework.util.StringUtils; | 27 | import org.springframework.util.StringUtils; |
28 | +import org.thingsboard.common.util.JacksonUtil; | ||
26 | import org.thingsboard.server.common.data.Device; | 29 | import org.thingsboard.server.common.data.Device; |
27 | import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; | 30 | import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
28 | import org.thingsboard.server.common.data.id.DeviceId; | 31 | import org.thingsboard.server.common.data.id.DeviceId; |
@@ -33,7 +36,6 @@ import org.thingsboard.server.common.msg.EncryptionUtil; | @@ -33,7 +36,6 @@ import org.thingsboard.server.common.msg.EncryptionUtil; | ||
33 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 36 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
34 | import org.thingsboard.server.dao.exception.DataValidationException; | 37 | import org.thingsboard.server.dao.exception.DataValidationException; |
35 | import org.thingsboard.server.dao.service.DataValidator; | 38 | import org.thingsboard.server.dao.service.DataValidator; |
36 | -import org.thingsboard.common.util.JacksonUtil; | ||
37 | 39 | ||
38 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; | 40 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; |
39 | import static org.thingsboard.server.dao.service.Validator.validateId; | 41 | import static org.thingsboard.server.dao.service.Validator.validateId; |
@@ -76,7 +78,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen | @@ -76,7 +78,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen | ||
76 | } | 78 | } |
77 | 79 | ||
78 | private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { | 80 | private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { |
79 | - if(deviceCredentials.getCredentialsType() == null){ | 81 | + if (deviceCredentials.getCredentialsType() == null) { |
80 | throw new DataValidationException("Device credentials type should be specified"); | 82 | throw new DataValidationException("Device credentials type should be specified"); |
81 | } | 83 | } |
82 | switch (deviceCredentials.getCredentialsType()) { | 84 | switch (deviceCredentials.getCredentialsType()) { |
@@ -140,7 +142,18 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen | @@ -140,7 +142,18 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen | ||
140 | } | 142 | } |
141 | 143 | ||
142 | private void formatSimpleLwm2mCredentials(DeviceCredentials deviceCredentials) { | 144 | private void formatSimpleLwm2mCredentials(DeviceCredentials deviceCredentials) { |
143 | - | 145 | + ObjectNode json = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), ObjectNode.class); |
146 | + JsonNode client = json.get("client"); | ||
147 | + if (client != null && client.get("securityConfigClientMode").asText().equals("X509") && client.has("cert")) { | ||
148 | + JsonNode certJson = client.get("cert"); | ||
149 | + if (!certJson.isNull()) { | ||
150 | + String cert = EncryptionUtil.trimNewLines(certJson.asText()); | ||
151 | + String sha3Hash = EncryptionUtil.getSha3Hash(cert); | ||
152 | + deviceCredentials.setCredentialsId(sha3Hash); | ||
153 | + ((ObjectNode) client).put("cert", cert); | ||
154 | + deviceCredentials.setCredentialsValue(JacksonUtil.toString(json)); | ||
155 | + } | ||
156 | + } | ||
144 | } | 157 | } |
145 | 158 | ||
146 | @Override | 159 | @Override |