From 6f32942f37a89368dedcd6db753c533e35921142 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 30 Aug 2021 19:08:18 +0300 Subject: [PATCH 01/21] lwm2m: fix bug execute --- .../lwm2m/server/LwM2mTransportUtil.java | 7 +++++++ .../DefaultLwM2mDownlinkMsgHandler.java | 21 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 8b65e55ad5..0fe6b67642 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -23,8 +23,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; +import org.eclipse.leshan.core.model.LwM2mModel; +import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.model.StaticModel; import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; @@ -522,4 +525,8 @@ public class LwM2mTransportUtil { return opaque.length() > 1024 ? opaque.substring(0, 1024) : opaque; } + public static LwM2mModel createModelsDefault() { + return new StaticModel(ObjectLoader.loadDefault()); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 5a6ac6b466..2ef832333b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -21,6 +21,7 @@ import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.LwM2m; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; +import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mObjectInstance; @@ -95,7 +96,9 @@ import static org.eclipse.leshan.core.attributes.Attribute.STEP; import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createModelsDefault; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateVersionedId; @Slf4j @@ -195,9 +198,20 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im @Override public void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback) { try { + validateVersionedId(client, request); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(request.getVersionedId())); ResourceModel resourceModelExecute = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); - if (resourceModelExecute != null) { - validateVersionedId(client, request); + if (resourceModelExecute == null) { + LwM2mModel model = createModelsDefault(); + if (pathIds.isResource()) { + resourceModelExecute = model.getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); + } + } + if (resourceModelExecute == null) { + callback.onValidationError(request.toString(), "ResourceModel with " + request.getVersionedId() + " is absent in system. Need ddd Lwm2m Model with id=" + + pathIds.getObjectId() + " ver=" + getVerFromPathIdVerOrId(request.getVersionedId()) + " to profile."); + } + else if (resourceModelExecute.operations.isExecutable()) { ExecuteRequest downlink; if (request.getParams() != null && !resourceModelExecute.multiple) { downlink = new ExecuteRequest(request.getObjectId(), (String) this.converter.convertValue(request.getParams(), resourceModelExecute.type, ResourceModel.Type.STRING, new LwM2mPath(request.getObjectId()))); @@ -206,6 +220,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im } sendSimpleRequest(client, downlink, request.getTimeout(), callback); } + else { + callback.onValidationError(request.toString(), "Resource with " + request.getVersionedId() + " is not executable."); + } } catch (InvalidRequestException e) { callback.onValidationError(request.toString(), e.getMessage()); } From bed8881d93b9a1ce90f2e9dd0546a5a506a5175f Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 30 Aug 2021 20:48:36 +0300 Subject: [PATCH 02/21] lwm2m: abstract test create --- ...a => AbstractRpcLwM2MIntegrationTest.java} | 12 +- .../sql/RpcLwm2mIntegrationCreateTest.java | 4 +- .../AbstractLwM2MIntegrationTest.java | 80 ++++++------ .../security/sql/OtaLwM2MIntegrationTest.java | 117 +++++++----------- .../DefaultLwM2MAttributesService.java | 2 +- 5 files changed, 93 insertions(+), 122 deletions(-) rename application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/{RpcAbstractLwM2MIntegrationTest.java => AbstractRpcLwM2MIntegrationTest.java} (99%) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/RpcAbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java similarity index 99% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/RpcAbstractLwM2MIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 36158b2640..f635de509b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/RpcAbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -60,19 +60,19 @@ import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST_BS; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT_BS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT_BS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID_BS; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0; @@ -84,7 +84,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; @DaoSqlTest -public class RpcAbstractLwM2MIntegrationTest extends AbstractWebsocketTest { +public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketTest { protected final String RPC_TRANSPORT_CONFIGURATION = "{\n" + " \"type\": \"LWM2M\",\n" + @@ -165,7 +165,7 @@ public class RpcAbstractLwM2MIntegrationTest extends AbstractWebsocketTest { protected String objectInstanceIdVer_5; protected String objectIdVer_19; - public RpcAbstractLwM2MIntegrationTest(){ } + public AbstractRpcLwM2MIntegrationTest(){ } @Before public void beforeTest() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java index a5672be133..5997449d1f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.eclipse.leshan.core.ResponseCode; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.transport.lwm2m.rpc.RpcAbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -28,7 +28,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInsta import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; -public class RpcLwm2mIntegrationCreateTest extends RpcAbstractLwM2MIntegrationTest { +public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTest { /** diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java index 2c47d5cc80..522abba65a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.security; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.io.IOUtils; -import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; @@ -84,12 +83,9 @@ import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import static org.eclipse.leshan.client.object.Security.noSec; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; @DaoSqlTest public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -175,7 +171,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest protected static final String ENDPOINT = "deviceAEndpoint"; - protected CoapClient client; + protected LwM2MTestClient client; public AbstractLwM2MIntegrationTest() { // create client credentials @@ -366,50 +362,46 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest public void after() { executor.shutdownNow(); wsClient.close(); + if (client != null) { + client.destroy(); + } } public void basicTestConnectionObserveTelemetry(Security security, LwM2MClientCredentials credentials, NetworkConfig coapConfig, String endpoint) throws Exception { - LwM2MTestClient client = null; - try { - createDeviceProfile(TRANSPORT_CONFIGURATION); - Device device = createDevice(credentials); - - SingleEntityFilter sef = new SingleEntityFilter(); - sef.setSingleEntity(device.getId()); - LatestValueCmd latestCmd = new LatestValueCmd(); - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - wsClient.send(mapper.writeValueAsString(wrapper)); - wsClient.waitForReply(); - - wsClient.registerWaitForUpdate(); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(security, coapConfig, clientPort); - String msg = wsClient.waitForUpdate(); - - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); - Assert.assertEquals(1, update.getCmdId()); - List eData = update.getUpdate(); - Assert.assertNotNull(eData); - Assert.assertEquals(1, eData.size()); - Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); - Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); - } finally { - if(client != null) { - client.destroy(); - } - } + createDeviceProfile(TRANSPORT_CONFIGURATION); + Device device = createDevice(credentials); + + SingleEntityFilter sef = new SingleEntityFilter(); + sef.setSingleEntity(device.getId()); + LatestValueCmd latestCmd = new LatestValueCmd(); + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); + + wsClient.send(mapper.writeValueAsString(wrapper)); + wsClient.waitForReply(); + + wsClient.registerWaitForUpdate(); + client = new LwM2MTestClient(executor, endpoint); + int clientPort = SocketUtils.findAvailableTcpPort(); + client.init(security, coapConfig, clientPort); + String msg = wsClient.waitForUpdate(); + + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); + Assert.assertEquals(1, update.getCmdId()); + List eData = update.getUpdate(); + Assert.assertNotNull(eData); + Assert.assertEquals(1, eData.size()); + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); + Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java index a8a23707c5..ffaeb3f5c6 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java @@ -134,14 +134,11 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @Test public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception { - LwM2MTestClient client = null; String endpoint = "WithoutFirmwareInfoDevice"; - try { createDeviceProfile(TRANSPORT_CONFIGURATION); NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); clientCredentials.setEndpoint(endpoint); Device device = createDevice(clientCredentials); - client = new LwM2MTestClient(executor, endpoint); int clientPort = SocketUtils.findAvailableTcpPort(); client.init(SECURITY, COAP_CONFIG, clientPort); @@ -161,48 +158,35 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { List expectedStatuses = Collections.singletonList(FAILED); Assert.assertEquals(expectedStatuses, statuses); - } finally { - if (client != null) { - client.destroy(); - } - } } @Test public void testFirmwareUpdateByObject5() throws Exception { - LwM2MTestClient client = null; String endpoint = "Ota5_Device"; List expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, UPDATING, UPDATED); - try { - createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(endpoint); - Device device = createDevice(clientCredentials); - - device.setFirmwareId(createFirmware().getId()); - Device saveDevice = doPost("/api/device", device, Device.class); - Thread.sleep(1000); + createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); + NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); + clientCredentials.setEndpoint(endpoint); + Device device = createDevice(clientCredentials); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(SECURITY, COAP_CONFIG, clientPort); + device.setFirmwareId(createFirmware().getId()); + Device saveDevice = doPost("/api/device", device, Device.class); + Thread.sleep(1000); - List ts = await("await on timeseries") - .atMost(30, TimeUnit.SECONDS) - .until(()-> toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + saveDevice.getId().getId() + "/values/timeseries?orderBy=ASC&keys=fw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() { - })), hasSize(expectedStatuses.size())); + client = new LwM2MTestClient(executor, endpoint); + int clientPort = SocketUtils.findAvailableTcpPort(); + client.init(SECURITY, COAP_CONFIG, clientPort); + List ts = await("await on timeseries") + .atMost(30, TimeUnit.SECONDS) + .until(() -> toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + saveDevice.getId().getId() + "/values/timeseries?orderBy=ASC&keys=fw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() { + })), hasSize(expectedStatuses.size())); - List statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); + List statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); - Assert.assertEquals(expectedStatuses, statuses); - } finally { - if (client != null) { - client.destroy(); - } - } + Assert.assertEquals(expectedStatuses, statuses); } /** @@ -215,44 +199,39 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { //given final List expectedStatuses = List.of( QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED); - LwM2MTestClient client = null; String endpoint = "Ota9_Device"; - try { - createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(endpoint); - final Device device = createDevice(clientCredentials); - device.setSoftwareId(createSoftware().getId()); - - final Device savedDevice = doPost("/api/device", device, Device.class); //sync call - assertThat(savedDevice).as("saved device").isNotNull(); - assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); - - //when - log.warn("Init the client..."); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(SECURITY, COAP_CONFIG, clientPort); - - log.warn("AWAIT atMost {} SECONDS on timeseries List by API with list size {}...", TIMEOUT, expectedStatuses.size()); - List ts = await("await on timeseries") - .atMost(30, TimeUnit.SECONDS) - .until(() -> getSwStateTelemetryFromAPI(device.getId().getId()), hasSize(expectedStatuses.size())); - log.warn("Got the ts: {}", ts); - - ts.sort(Comparator.comparingLong(TsKvEntry::getTs)); - log.warn("Ts ordered: {}", ts); - ts.forEach((x) -> log.warn("ts: {} ", x)); - - List statuses = ts.stream().map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); - log.warn("Converted ts to statuses: {}", statuses); - - assertThat(statuses).isEqualTo(expectedStatuses); - } finally { - if (client != null) { - client.destroy(); - } - } + createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); + NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); + clientCredentials.setEndpoint(endpoint); + final Device device = createDevice(clientCredentials); + device.setSoftwareId(createSoftware().getId()); + + final Device savedDevice = doPost("/api/device", device, Device.class); //sync call + Thread.sleep(1000); + + assertThat(savedDevice).as("saved device").isNotNull(); + assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); + + //when + log.warn("Init the client..."); + client = new LwM2MTestClient(executor, endpoint); + int clientPort = SocketUtils.findAvailableTcpPort(); + client.init(SECURITY, COAP_CONFIG, clientPort); + + log.warn("AWAIT atMost {} SECONDS on timeseries List by API with list size {}...", TIMEOUT, expectedStatuses.size()); + List ts = await("await on timeseries") + .atMost(30, TimeUnit.SECONDS) + .until(() -> getSwStateTelemetryFromAPI(device.getId().getId()), hasSize(expectedStatuses.size())); + log.warn("Got the ts: {}", ts); + + ts.sort(Comparator.comparingLong(TsKvEntry::getTs)); + log.warn("Ts ordered: {}", ts); + ts.forEach((x) -> log.warn("ts: {} ", x)); + + List statuses = ts.stream().map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); + log.warn("Converted ts to statuses: {}", statuses); + + assertThat(statuses).isEqualTo(expectedStatuses); } private Device getDeviceFromAPI(UUID deviceId) throws Exception { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index 3d27621c00..f70af7bf0e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -198,7 +198,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { } } } catch (IllegalArgumentException e){ - log.error("Failed update resource [{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), e.getMessage()); + log.error("Failed update resource ["+lwM2MClient.getEndpoint()+"] onAttributesUpdate:", e); String logMsg = String.format("%s: Failed update resource onAttributesUpdate %s.", LOG_LWM2M_ERROR, e.getMessage()); logService.log(lwM2MClient, logMsg); From c772929abc96e9eca13030d18c89a2ce253bc87d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 31 Aug 2021 00:47:58 +0300 Subject: [PATCH 03/21] lwm2m: abstract test write, observe, discover, read --- .../transport/lwm2m/Lwm2mTestHelper.java | 10 +- .../sql/RpcLwm2mIntegrationDiscoverTest.java | 156 +++++++++ .../sql/RpcLwm2mIntegrationObserveTest.java | 190 ++++++++++ .../rpc/sql/RpcLwm2mIntegrationReadTest.java | 226 ++++++++++++ .../rpc/sql/RpcLwm2mIntegrationWriteTest.java | 330 ++++++++++++++++++ 5 files changed, 907 insertions(+), 5 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 37c126df21..3fb5b1a1e6 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -49,13 +49,13 @@ public class Lwm2mTestHelper { public static final int objectInstanceId_1 = 1; public static final int objectInstanceId_12 = 12; public static final int resourceId_0 = 0; - protected static final int resourceId_1 = 1; - protected static final int resourceId_2 = 2; - protected static final int resourceId_3 = 3; + public static final int resourceId_1 = 1; + public static final int resourceId_2 = 2; + public static final int resourceId_3 = 3; public static final int resourceId_9 = 9; - protected static final int resourceId_11 = 11; + public static final int resourceId_11 = 11; public static final int resourceId_14 = 14; - protected static final int resourceId_15= 15; + public static final int resourceId_15= 15; public static final String resourceIdName_3_9 = "batteryLevel"; public static final String resourceIdName_3_14 = "UtfOffset"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java new file mode 100644 index 0000000000..64acf699ee --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java @@ -0,0 +1,156 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; + + +public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegrationTest { + + /** + * DiscoverAll + * + * @throws Exception + */ + @Test + public void testDiscoverAll_Return_CONTENT_LinksAllObjectsAllInstancesOfClient() throws Exception { + String setRpcRequest = "{\"method\":\"DiscoverAll\"}"; + String actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + JsonNode rpcActualValue = JacksonUtil.toJsonNode(rpcActualResult.get("value").asText()); + Set actualObjects = ConcurrentHashMap.newKeySet(); + Set actualInstances = ConcurrentHashMap.newKeySet(); + rpcActualValue.forEach(node -> { + if (!node.get("url").asText().equals("/")) { + LwM2mPath path = new LwM2mPath(node.get("url").asText()); + actualObjects.add("/" + path.getObjectId()); + if (path.isObjectInstance()) { + actualInstances.add("/" + path.getObjectId() + "/" + path.getObjectInstanceId()); + } + } + }); + assertEquals(expectedInstances, actualInstances); + assertEquals(expectedObjects, actualObjects); + } + + /** + * Discover {"id":"/3"} + * + * @throws Exception + */ + @Test + public void testDiscoverObject_Return_CONTENT_LinksInstancesAndResourcesOnLyExpectedObject() { + expectedObjectIdVers.forEach(expected -> { + try { + String actualResult = sendDiscover((String) expected); + String expectedObjectId = objectIdVerToObjectId ((String) expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String[] actualValues = rpcActualResult.get("value").asText().split(","); + assertTrue(actualValues.length > 0); + assertEquals(0, Arrays.stream(actualValues).filter(path -> !path.contains(expectedObjectId)).collect(Collectors.toList()).size()); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + /** + * Discover {"id":"3/0"} + * + * @throws Exception + */ + @Test + public void testDiscoverInstance_Return_CONTENT_LinksResourcesOnLyExpectedInstance() throws Exception { + String expected = (String) expectedObjectIdVerInstances.stream().findAny().get(); + String actualResult = sendDiscover(expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expectedObjectInstanceId = objectInstanceIdVerToObjectInstanceId (expected); + String[] actualValues = rpcActualResult.get("value").asText().split(","); + assertTrue(actualValues.length > 0); + assertEquals(0, Arrays.stream(actualValues).filter(path -> !path.contains(expectedObjectInstanceId)).collect(Collectors.toList()).size()); + } + + /** + * Discover {"id":"3/0/14"} + * + * @throws Exception + */ + @Test + public void testDiscoverResource_Return_CONTENT_LinksResourceOnLyExpectedResource() throws Exception { + String expectedInstance = (String) expectedInstances.stream().findFirst().get(); + String expectedObjectInstanceId = objectInstanceIdVerToObjectInstanceId (expectedInstance); + LwM2mPath expectedPath = new LwM2mPath(expectedObjectInstanceId); + int expectedResource = client.getClient().getObjectTree().getObjectEnablers().get(expectedPath.getObjectId()).getObjectModel().resources.entrySet().stream().findAny().get().getKey(); + String expected = expectedInstance + "/" + expectedResource; + String actualResult = sendDiscover(expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expectedResourceId = "<" + expectedObjectInstanceId + "/" + expectedResource + ">"; + String actualValue = rpcActualResult.get("value").asText(); + assertEquals(expectedResourceId, actualValue ); + + } + + /** + * Discover {"id":"2/0/2"} + * + * @throws Exception + */ + @Test + public void testDiscoverInstanceAbsentInObject_Return_NOT_FOUND() throws Exception { + String expected = objectIdVer_2 + "/" + objectInstanceId_0; + String actualResult = sendDiscover(expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); + } + /** + * Discover {"id":"2/0/2"} + * + * @throws Exception + */ + @Test + public void testDiscoverResourceAbsentInObject_Return_NOT_FOUND() throws Exception { + String expected = objectIdVer_2 + "/" + objectInstanceId_0 + "/" + resourceId_2; + String actualResult = sendDiscover(expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); + } + + private String sendDiscover(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Discover\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java new file mode 100644 index 0000000000..848a9563ed --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java @@ -0,0 +1,190 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; + +public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationTest { + + /** + * ObserveReadAll + * @throws Exception + */ + @Test + public void testObserveReadAllNothingObservation_Result_CONTENT_Value_Count_0() throws Exception { + String actualResult = sendObserve("ObserveCancelAll", null); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + actualResult = sendObserve("ObserveReadAll", null); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + assertEquals("[]", rpcActualResult.get("value").asText()); + } + + /** + * Observe {"id":"/3/0/9"} + * @throws Exception + */ + @Test + public void testObserveSingleResource_Result_CONTENT_Value_SingleResource() throws Exception { + String expectedIdVer = objectInstanceIdVer_3 + "/" + resourceId_9; + String actualResult = sendObserve("Observe", expectedIdVer); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + assertTrue(rpcActualResult.get("value").asText().contains("LwM2mSingleResource")); + } + + /** + * Observe {"id":"/3_1.1/0/13"} + * @throws Exception + */ + @Test + public void testObserveWithBadVersion_Result_BadRequest_ErrorMsg_BadVersionMustBe1_0() throws Exception { + String expectedInstance = (String) expectedInstances.stream().filter(path -> !((String)path).contains("_")).findFirst().get(); + LwM2mPath expectedPath = new LwM2mPath(expectedInstance); + int expectedResource = client.getClient().getObjectTree().getObjectEnablers().get(expectedPath.getObjectId()).getObjectModel().resources.entrySet().stream().findAny().get().getKey(); + String expectedId = "/" + expectedPath.getObjectId() + "_1.2" + "/" + expectedPath.getObjectInstanceId() + "/" + expectedResource; + String actualResult = sendObserve("Observe", expectedId); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expected = "Specified resource id " + expectedId +" is not valid version! Must be version: 1.0"; + assertEquals(expected, rpcActualResult.get("error").asText()); + } + + /** + * Not implemented Instance + * Observe {"id":"/2/0"} + * @throws Exception + */ + @Test + public void testObserveNoImplementedInstanceOnDevice_Result_NotFound() throws Exception { + String objectInstanceIdVer = (String) expectedObjectIdVers.stream().filter(path -> ((String)path).contains("/" + ACCESS_CONTROL)).findFirst().get(); + String expected = objectInstanceIdVer + "/" + objectInstanceId_0; + String actualResult = sendObserve("Observe", expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); + } + + /** + * Not implemented Resource + * Observe {"id":"/19_1.1/0/0"} + * @throws Exception + */ + @Test + public void testObserveNoImplementedResourceOnDeviceValueNull_Result_BadRequest() throws Exception { + String objectIdVer = (String) expectedObjectIdVers.stream().filter(path -> ((String)path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); + String expected = objectIdVer + "/" + objectInstanceId_0 + "/" + resourceId_0; + String actualResult = sendObserve("Observe", expected); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String expectedValue = "values MUST NOT be null"; + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + assertEquals(expectedValue, rpcActualResult.get("error").asText()); + } + + /** + * Repeated request on Observe + * Observe {"id":"/5/0/0"} + * @throws Exception + */ + @Test + public void testObserveRSourceNotRead_Result_METHOD_NOT_ALLOWED() throws Exception { + String expectedId = objectInstanceIdVer_5 + "/" + resourceId_0; + sendObserve("Observe", expectedId); + String actualResult = sendObserve("Observe", expectedId); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * Repeated request on Observe + * Observe {"id":"/3/0/0"} + * @throws Exception + */ + @Test + public void testObserveRepeatedRequestObserveOnDevice_Result_BAD_REQUEST_ErrorMsg_AlreadyRegistered() throws Exception { + String expectedId = objectInstanceIdVer_3 + "/" + resourceId_0; + sendObserve("Observe", expectedId); + String actualResult = sendObserve("Observe", expectedId); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expected = "Observation is already registered!"; + assertEquals(expected, rpcActualResult.get("error").asText()); + } + + /** + * ObserveReadAll + * @throws Exception + */ + @Test + public void testObserveReadAll_Result_CONTENT_Value_Contains_Paths_Count_ObserveAll() throws Exception { + sendObserve("ObserveCancelAll", null); + String expectedId_0 = objectInstanceIdVer_3 + "/" + resourceId_0; + String expectedId_9 = objectInstanceIdVer_3 + "/" + resourceId_9; + sendObserve("Observe", expectedId_0); + sendObserve("Observe", expectedId_9); + String actualResult = sendObserve("ObserveReadAll", null); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String actualValues = rpcActualResult.get("value").asText(); + assertTrue(actualValues.contains(expectedId_0)); + assertTrue(actualValues.contains(expectedId_9)); + assertEquals(2, actualValues.split(",").length); + } + + + /** + * ObserveCancel {"id":"/3/0/3"} + * ObserveCancel {"id":"/5/0/3"} + */ + @Test + public void testObserveCancelOneResource_Result_CONTENT_Value_Count_1() throws Exception { + sendObserve("ObserveCancelAll", null); + String expectedId_0 = objectInstanceIdVer_3 + "/" + resourceId_0; + String expectedId_3 = objectInstanceIdVer_5 + "/" + resourceId_3; + sendObserve("Observe", expectedId_0); + sendObserve("Observe", expectedId_3); + String actualResult = sendObserve("ObserveCancel", expectedId_0); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + assertEquals("1", rpcActualResult.get("value").asText()); + } + + private String sendObserve(String method, String params) throws Exception { + String sendRpcRequest; + if (params == null) { + sendRpcRequest = "{\"method\": \"" + method + "\"}"; + } + else { + sendRpcRequest = "{\"method\": \"" + method + "\", \"params\": {\"id\": \"" + params + "\"}}"; + } + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, sendRpcRequest, String.class, status().isOk()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java new file mode 100644 index 0000000000..4f696e1c3c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -0,0 +1,226 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import static org.eclipse.leshan.core.LwM2mId.SERVER; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_11; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; + + +public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest { + + /** + * Read {"id":"/3"} + * Read {"id":"/6"}... + */ + @Test + public void testReadAllObjectsInClientById_Result_CONTENT_Value_IsLwM2mObject_IsInstances() throws Exception { + expectedObjectIdVers.forEach(expected -> { + try { + String actualResult = sendRPCById((String) expected); + String expectedObjectId = objectIdVerToObjectId ((String) expected); + LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources="; + if (expectedPath.getObjectId() == 2) { + expectedObjectInstances = "LwM2mObject [id=2, instances={}]"; + } + assertTrue(rpcActualResult.get("value").asText().contains(expectedObjectInstances)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + /** + * Read {"id":"/5/0"} + * + * @throws Exception + */ + @Test + public void testReadAllInstancesInClientById_Result_CONTENT_Value_IsInstances_IsResources() throws Exception{ + expectedObjectIdVerInstances.forEach(expected -> { + try { + String actualResult = sendRPCById((String) expected); + String expectedObjectId = objectInstanceIdVerToObjectInstanceId ((String) expected); + LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expectedObjectInstances = "LwM2mObjectInstance [id=" + expectedPath.getObjectInstanceId() + ", resources={"; + assertTrue(rpcActualResult.get("value").asText().contains(expectedObjectInstances)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + /** + * Read {"id":"/19/1/0"} + * + * @throws Exception + */ + @Test + public void testReadMultipleResourceById_Result_CONTENT_Value_IsLwM2mMultipleResource() throws Exception { + String expectedIdVer = objectInstanceIdVer_3 +"/" + resourceId_11 ; + String actualResult = sendRPCById(expectedIdVer); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expected = "LwM2mMultipleResource [id=" + resourceId_11 + ", values={"; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); + } + + /** + * Read {"id":"/3/0/14"} + */ + @Test + public void testReadSingleResourceById_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception { + String expectedIdVer = objectInstanceIdVer_3 +"/" + resourceId_14 ; + String actualResult = sendRPCById(expectedIdVer); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value="; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); + } + + /** + * Read {"key":"UtfOffset"} + */ + @Test + public void testReadSingleResourceByKey_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception { + String expectedKey = resourceIdName_3_14 ; + String actualResult = sendRPCByKey(expectedKey); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value="; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); + } + + /** + * ReadComposite {"ids":["/1_1.2", "/3_1.0/0/1", "/3_1.0/0/11"]} + */ + @Test + public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectIsLwM2mSingleResourceIsLwM2mMultipleResource() throws Exception { + String expectedIdVer_1 = (String) expectedObjectIdVers.stream().filter(path -> (!((String)path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String)path).contains("/" + SERVER))).findFirst().get(); + String objectId_1 = objectIdVerToObjectId(expectedIdVer_1); + String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + resourceId_1; + String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + resourceId_11; + String objectInstanceId_3 = objectIdVerToObjectId(objectInstanceIdVer_3); + String expectedIds = "[\"" + expectedIdVer_1 + "\", \"" + expectedIdVer3_0_1 + "\", \"" + expectedIdVer3_0_11 + "\"]"; + String actualResult = sendCompositeRPCByIds(expectedIds); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expected1 = objectId_1 + "=LwM2mObject [id=" + new LwM2mPath(objectId_1).getObjectId() + ", instances={"; + String expected3_0_1 = objectInstanceId_3 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value="; + String expected3_0_11 = objectInstanceId_3 + "/" + resourceId_11 + "=LwM2mMultipleResource [id=" + resourceId_11 + ", values={"; + String actualValues = rpcActualResult.get("value").asText(); + assertTrue(actualValues.contains(expected1)); + assertTrue(actualValues.contains(expected3_0_1)); + assertTrue(actualValues.contains(expected3_0_11)); + } + + /** + * ReadComposite {"ids":["/1_1.2/0/1", "/1_1.2/0/2", "/3_1.0/0"]} + */ + @Test + public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectInstanceIsLwM2mSingleResource() throws Exception { + String expectedIdVer3_0 = objectInstanceIdVer_3; + String expectedIdVer1_0_1 = objectInstanceIdVer_1 + "/" + resourceId_1; + String expectedIdVer1_0_2 = objectInstanceIdVer_1 + "/" + resourceId_2; + String expectedIds = "[\"" + expectedIdVer1_0_1 + "\", \"" + expectedIdVer1_0_2 + "\", \"" + expectedIdVer3_0 + "\"]"; + String actualResult = sendCompositeRPCByIds(expectedIds); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String objectInstanceId_3 = objectInstanceIdVerToObjectInstanceId(objectInstanceIdVer_3); + LwM2mPath path = new LwM2mPath(objectInstanceId_3); + String expected3_0 = objectInstanceId_3 + "=LwM2mObjectInstance [id=" + path.getObjectInstanceId() + ", resources={"; + String objectInstanceId_1 = objectInstanceIdVerToObjectInstanceId(objectInstanceIdVer_1); + String expected1_0_1 = objectInstanceId_1 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value="; + String expected1_0_2 = objectInstanceId_1 + "/" + resourceId_2 + "=null"; + String actualValues = rpcActualResult.get("value").asText(); + assertTrue(actualValues.contains(expected3_0)); + assertTrue(actualValues.contains(expected1_0_1)); + assertTrue(actualValues.contains(expected1_0_2)); + } + + /** + * ReadComposite {"keys":["batteryLevel", "UtfOffset", "dataRead", "dataWrite"]} + */ + @Test + public void testReadCompositeSingleResourceByKeys_Result_CONTENT_Value_3_0_IsLwM2mSingleResource_19_0_0_AND_19_0_1_Null() throws Exception { + String expectedKey3_0_9 = resourceIdName_3_9; + String expectedKey3_0_14 = resourceIdName_3_14; + String expectedKey19_0_0 = resourceIdName_19_0_0; + String expectedKey19_1_0 = resourceIdName_19_1_0; + String expectedKeys = "[\"" + expectedKey3_0_9 + "\", \"" + expectedKey3_0_14 + "\", \"" + expectedKey19_0_0 + "\", \"" + expectedKey19_1_0 + "\"]"; + String actualResult = sendCompositeRPCByKeys(expectedKeys); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String objectInstanceId_3 = objectInstanceIdVerToObjectInstanceId(objectInstanceIdVer_3); + String objectId_19 = objectIdVerToObjectId(objectIdVer_19); + String expected3_0_9 = objectInstanceId_3 + "/" + resourceId_9 + "=LwM2mSingleResource [id=" + resourceId_9 + ", value="; + String expected3_0_14 = objectInstanceId_3 + "/" + resourceId_14 + "=LwM2mSingleResource [id=" + resourceId_14 + ", value="; + String expected19_0_0 = objectId_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "=null"; + String expected19_1_0 = objectId_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "=null"; + String actualValues = rpcActualResult.get("value").asText(); + assertTrue(actualValues.contains(expected3_0_9)); + assertTrue(actualValues.contains(expected3_0_14)); + assertTrue(actualValues.contains(expected19_0_0)); + assertTrue(actualValues.contains(expected19_1_0)); + } + + + private String sendRPCById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendRPCByKey(String key) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"key\": \"" + key + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendCompositeRPCByIds(String paths) throws Exception { + String setRpcRequest = "{\"method\": \"ReadComposite\", \"params\": {\"ids\":" + paths + "}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendCompositeRPCByKeys(String keys) throws Exception { + String setRpcRequest = "{\"method\": \"ReadComposite\", \"params\": {\"keys\":" + keys + "}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java new file mode 100644 index 0000000000..0150572351 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java @@ -0,0 +1,330 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_15; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; + +public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTest { + + + /** + * id + * WriteReplace {"id":"3/0/14","value":"+12"} + */ + @Test + public void testWriteReplaceValueSingleResourceById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_14; + String expectedValue = "+12"; + String actualResult = sendRPCWriteStringById("WriteReplace", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadById(expectedPath); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + } + + /** + * key + * WriteReplace {"key":"timezone","value":"+10"} + */ + @Test + public void testWriteReplaceValueSingleResourceByKey_Result_CHANGED() throws Exception { + String expectedKey = resourceIdName_3_14; + String expectedValue = "+09"; + String actualResult = sendRPCWriteByKey("WriteReplace", expectedKey, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadByKey(expectedKey); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + } + + + /** + * id + * WriteReplace {"id": "/19_1.1/0/0","value": {"0":"0000ad45675600", "15":"1525ad45675600cdef"}} + */ + @Test + public void testWriteReplaceValueMultipleResource_Result_CHANGED_Value_Multi_Instance_Resource_must_in_Json_format() throws Exception { + String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0; + int resourceInstanceId0 = 0; + int resourceInstanceId15 = 15; + String expectedValue0 = "0000ad45675600"; + String expectedValue15 = "1525ad45675600cdef"; + String expectedValue = "{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId15 + "\":\"" + expectedValue15 + "\"}"; + String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + String expectedPath0 = expectedPath + "/" + resourceInstanceId0; + String expectedPath15 = expectedPath + "/" + resourceInstanceId15; + actualResult = sendRPCReadById(expectedPath0); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadById(expectedPath15); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mResourceInstance [id=" + resourceInstanceId15 + ", value=" + expectedValue15.length()/2 + "Bytes, type=OPAQUE]"; + assertTrue(actualValues.contains(expected)); + } + + /** + * id + * WriteReplace {"id":"/19/0/0","value":"0081"}.. + */ + @Test + public void testWriteReplaceValueMultipleResourceAsSingleResource_Result_BAD_REQUEST_Value_Multi_Instance_Resource_must_be_in_Json_format() throws Exception { + String objectInstanceIdVer_19 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); + String expectedPath = objectInstanceIdVer_19 + "/" + resourceId_0; + String expectedValue = "0081"; + String actualResult = sendRPCWriteStringById("WriteReplace", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String actualValues = rpcActualResult.get("error").asText(); + String expected = "Resource value is: String. Value of Multi-Instance Resource must be in Json format!"; + assertTrue(actualValues.contains(expected)); + } + + + /** + * bad - only read + * WriteReplace {"id":"/3/0/9","value":90} + * {"result":"METHOD_NOT_ALLOWED"} + */ + @Test + public void testWriteReplaceValueSingleResourceR_ById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_9; + Integer expectedValue = 90; + String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * ids + * WriteUpdate {"id":"/3/0","value":{"14":"+5","15":"Kiyv/Europe"}} + * + */ + @Test + public void testWriteUpdateValueSingleResourceById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_3; + String expectedValue14 = "+5"; + String expectedValue15 = "Kiyv/Europe"; + String expectedValue = "{\"" + resourceId_14 + "\":\"" + expectedValue14 + "\",\"" + resourceId_15 + "\":\"" + expectedValue15 + "\"}"; + String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + String expectedPath14 = objectInstanceIdVer_3 + "/" + resourceId_14; + String expectedPath15 = objectInstanceIdVer_3 + "/" + resourceId_15; + actualResult = sendRPCReadById(expectedPath14); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue14 + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadById(expectedPath15); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue15 + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + } + + /** + * id + * WriteUpdate {"id": "/19_1.1/0","value": {"0":{"0":"00ad456756", "25":"25ad456756"}}} + */ + @Test + public void testWriteUpdateValueMultipleResourceById_Result_CHANGED() throws Exception { + String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0; + int resourceInstanceId0 = 0; + int resourceInstanceId25 = 25; + String expectedValue0 = "00ad45675600"; + String expectedValue25 = "25ad45675600cdef"; + String expectedValue = "{\"" + resourceId_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}"; + String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + String expectedPath0 = expectedPath + "/" + resourceId_0 + "/" + resourceInstanceId0; + String expectedPath25 =expectedPath + "/" + resourceId_0 + "/" + resourceInstanceId25; + actualResult = sendRPCReadById(expectedPath0); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadById(expectedPath25); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length()/2 + "Bytes, type=OPAQUE]"; + assertTrue(actualValues.contains(expected)); + } + + /** + * ResourceInstance + KeySingleResource + IdSingleResource + * WriteComposite {"nodes":{"/19/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}} + */ + @Test + public void testWriteCompositeValueSingleResourceResourceInstanceByIdKey_Result_CHANGED() throws Exception { + int resourceInstanceId2 = 2; + String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId2; + String expectedValue19_1_0_2 = "00001234"; + String expectedKey3_0_14 = resourceIdName_3_14; + String expectedValue3_0_14 = "+04"; + String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedValue3_0_15 = "Kiyv/Europe"; + String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + + "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; + String actualResult = sendCompositeRPC(nodes); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadById(expectedPath19_1_0_2); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadByKey(expectedKey3_0_14); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadById(expectedPath3_0_15); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + } + + /** + * multipleResource == error + * bad - cannot be used for value Json, only primitive: SingleResource, ResourceInstance (for Json: WriteUpdate, WriteReplace) + * WriteComposite {"nodes":{"/19/0/0":{"0":"abcd5678", "10":"abcd5678"}}} + */ + @Test + public void testWriteCompositeValueSingleMultipleResourceByIdKey_Result_BAD_REQUEST_WriteComposite_operation_for_SingleResources_or_and_ResourceInstance() throws Exception { + String nodes = "{\"/19/0/0\":{\"0\":\"abcd5678\", \"10\":\"abcd5678\"}}"; + String actualResult = sendCompositeRPC(nodes); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String actualValues = rpcActualResult.get("error").asText(); + String expectedNodes = nodes.replaceAll("\"", "").replaceAll(":", "="); + String expected = String.format("nodes: %s is not validate value. " + + "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", expectedNodes); + assertEquals(expected, actualValues); + } + + + /** + * create_2_instances_in_object + * new ObjectInstance if Object is Multiple & Resource Single + * Create {"id":"/19/2","value":{"1":2}} + * Create {"id":"/19/3","value":{"0":{"0":"00AC", "1":"ddff12"}}} + */ + @Test + public void testCreateObjectInstanceSingleByIdKey_Result_CHANGED() throws Exception { + int resourceInstanceId2 = 2; + String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId2; + String expectedValue19_1_0_2 = "00001234"; + String expectedKey3_0_14 = resourceIdName_3_14; + String expectedValue3_0_14 = "+04"; + String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedValue3_0_15 = "Kiyv/Europe"; + String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + + "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; + String actualResult = sendCompositeRPC(nodes); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadById(expectedPath19_1_0_2); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadByKey(expectedKey3_0_14); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + actualResult = sendRPCReadById(expectedPath3_0_15); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + actualValues = rpcActualResult.get("value").asText(); + expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]"; + assertTrue(actualValues.contains(expected)); + } + + /** + * failed: cannot_create_mandatory_single_object + * Create {"id":"/3/2","value":{"0":"00AC"}} + */ + + + /** + * failed: cannot_create_instance_of_security_object + * Create {"id":"/0/2","value":{"0":"00AC"}} + */ + + /** + * failed: cannot_create_instance_of_absent_object + * Create {"id":"/50/2","value":{"0":"00AC"}} + */ + + private String sendRPCWriteStringById(String method, String path, String value) throws Exception { + String setRpcRequest = "{\"method\": \"" + method + "\", \"params\": {\"id\": \"" + path + "\", \"value\": \"" + value + "\" }}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendRPCWriteObjectById(String method, String path, Object value) throws Exception { + String setRpcRequest = "{\"method\": \"" + method + "\", \"params\": {\"id\": \"" + path + "\", \"value\": " + value + " }}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendRPCReadById(String id) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + id + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendRPCWriteByKey(String method, String key, String value) throws Exception { + String setRpcRequest = "{\"method\": \"" + method + "\", \"params\": {\"key\": \"" + key + "\", \"value\": \"" + value + "\" }}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendRPCReadByKey(String key) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"key\": \"" + key + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendCompositeRPC(String nodes) throws Exception { + String setRpcRequest = "{\"method\": \"WriteComposite\", \"params\": {\"nodes\":" + nodes + "}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } +} \ No newline at end of file From 0a3c626333fe58a889d6bd16d170688920e23e88 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 31 Aug 2021 23:38:31 +0300 Subject: [PATCH 04/21] lwm2m: abstract integration RPC test delete, create, writeAttribute, execute --- .../transport/lwm2m/Lwm2mTestHelper.java | 5 + .../lwm2m/client/LwM2MTestClient.java | 1 - .../rpc/AbstractRpcLwM2MIntegrationTest.java | 34 +++- .../sql/RpcLwm2mIntegrationCreateTest.java | 70 ++++++- .../sql/RpcLwm2mIntegrationDeleteTest.java | 96 +++++++++ .../sql/RpcLwm2mIntegrationDiscoverTest.java | 35 ++-- .../sql/RpcLwm2mIntegrationExecuteTest.java | 186 ++++++++++++++++++ .../rpc/sql/RpcLwm2mIntegrationReadTest.java | 16 +- ...pcLwm2mIntegrationWriteAttributesTest.java | 63 ++++++ .../rpc/sql/RpcLwm2mIntegrationWriteTest.java | 71 ++++--- .../security/sql/OtaLwM2MIntegrationTest.java | 6 +- .../device/data/lwm2m/OtherConfiguration.java | 2 - .../lwm2m/server/client/LwM2mClient.java | 2 +- 13 files changed, 520 insertions(+), 67 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 3fb5b1a1e6..232332e554 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -45,17 +45,22 @@ public class Lwm2mTestHelper { public static final int TEMPERATURE_SENSOR = 3303; // Ids in Client + public static final int objectId_0 = 0; public static final int objectInstanceId_0 = 0; public static final int objectInstanceId_1 = 1; + public static final int objectInstanceId_2 = 2; public static final int objectInstanceId_12 = 12; public static final int resourceId_0 = 0; public static final int resourceId_1 = 1; public static final int resourceId_2 = 2; public static final int resourceId_3 = 3; + public static final int resourceId_4 = 4; + public static final int resourceId_8 = 8; public static final int resourceId_9 = 9; public static final int resourceId_11 = 11; public static final int resourceId_14 = 14; public static final int resourceId_15= 15; + public static final int resourceInstanceId_2 = 2; public static final String resourceIdName_3_9 = "batteryLevel"; public static final String resourceIdName_3_14 = "UtfOffset"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 4c5406785c..b79fb39799 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -44,7 +44,6 @@ import org.eclipse.leshan.core.request.BootstrapRequest; import org.eclipse.leshan.core.request.DeregisterRequest; import org.eclipse.leshan.core.request.RegisterRequest; import org.eclipse.leshan.core.request.UpdateRequest; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.junit.Assert; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index f635de509b..13bafd39d3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -59,6 +59,7 @@ import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.SERVER; +import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; @@ -72,6 +73,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID_BS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0; @@ -158,12 +160,16 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT public Set expectedObjectIdVerInstances; protected String objectInstanceIdVer_1; + protected String objectIdVer_0; protected String objectIdVer_2; private static final Predicate predicate_3 = path -> (!((String) path).contains("/" + TEMPERATURE_SENSOR) && ((String) path).contains("/" + DEVICE)); protected String objectIdVer_3; protected String objectInstanceIdVer_3; protected String objectInstanceIdVer_5; + protected String objectInstanceIdVer_9; protected String objectIdVer_19; + protected String objectIdVer_50 = "/50"; + protected String objectIdVer_3303; public AbstractRpcLwM2MIntegrationTest(){ } @@ -212,13 +218,21 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT }); } }); - objectInstanceIdVer_1 = (String) expectedObjectIdVerInstances.stream().filter(path -> (!((String) path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String) path).contains("/" + SERVER))).findFirst().get(); + String ver_Id_0 = client.getClient().getObjectTree().getModel().getObjectModel(objectId_0).version; + if ("1.0".equals(ver_Id_0)) { + objectIdVer_0 = "/" + objectId_0; + } + else { + objectIdVer_0 = "/" + objectId_0 + "_" + ver_Id_0; + } objectIdVer_2 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + ACCESS_CONTROL)).findFirst().get(); objectIdVer_3 = (String) expectedObjects.stream().filter(predicate_3).findFirst().get(); - objectIdVer_19 = (String) expectedObjects.stream().filter(predicate_3).findFirst().get(); + objectIdVer_19 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); + objectIdVer_3303 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + TEMPERATURE_SENSOR)).findFirst().get(); + objectInstanceIdVer_1 = (String) expectedObjectIdVerInstances.stream().filter(path -> (!((String) path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String) path).contains("/" + SERVER))).findFirst().get(); objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(predicate_3).findFirst().get(); objectInstanceIdVer_5 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + FIRMWARE)).findFirst().get(); - objectIdVer_19 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); + objectInstanceIdVer_9 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + SOFTWARE_MANAGEMENT)).findFirst().get(); } protected void createDeviceProfile(String transportConfiguration) throws Exception { @@ -285,13 +299,13 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT return bootstrap; } - protected String objectIdVerToObjectId(String objectIdVer) { - return objectIdVer.contains("_") ? objectIdVer.split("_")[0] : objectIdVer; - } - - protected String objectInstanceIdVerToObjectInstanceId(String objectInstanceIdVer) { - String[] objectIdVer = objectInstanceIdVer.split("/"); - return objectIdVer[1].contains("_") ? objectIdVer[1].split("_")[0] + "/" + objectIdVer[2] : objectInstanceIdVer; + protected String pathIdVerToObjectId(String pathIdVer) { + if (pathIdVer.contains("_")){ + String [] objVer = pathIdVer.split("/"); + objVer[1] = objVer[1].split("_")[0]; + return String.join("/", objVer); + } + return pathIdVer; } @After diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java index 5997449d1f..4ab0d68794 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql; import com.fasterxml.jackson.databind.node.ObjectNode; import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; @@ -25,11 +26,29 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTest { + /** + * Create {"id":"/19_1.1","value":{"0":{"0":"00AC"}, "1":1}} + * + * create_2_instances_in_object + * new ObjectInstance if Object is Multiple & Resource Single + * Create {"id":"/19_1.1/12","value":{"0":{"0":"00AC", "1":1}}} + * {"{"result":"CREATED"}"} + */ + @Test + public void testCreateObjectInstanceWithInstanceIdByIdKey_Result_CREATED() throws Exception { + String expectedPath = objectIdVer_19 + "/" + objectInstanceId_12; + String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; + String actualResult = sendRPCreateById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CREATED.getName(), rpcActualResult.get("result").asText()); + } /** * Create {"id":"/19_1.1","value":{"0":{"0":"00AC"}, "1":1}} @@ -38,10 +57,9 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe * new ObjectInstance if Object is Multiple & Resource Single * Create {"id":"/19_1.1/0","value":{"0":{"0":"00AC", "1":1}}} * {"result":"BAD_REQUEST","error":"instance 0 already exists"} - * */ @Test - public void testCreateObjectInstanceWithInstanceIdAlreadyExistsByIdKey_Result_BAD_REQUEST() throws Exception { + public void testCreateObjectInstanceWithInstanceIdAlreadyExistsById_Result_BAD_REQUEST() throws Exception { String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0; String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; String actualResult = sendRPCreateById(expectedPath, expectedValue); @@ -54,20 +72,58 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe /** * failed: cannot_create_mandatory_single_object - * Create {"id":"/3/2","value":{"0":"00AC"}} + * Create {"id":"/3/1,"value":{"0":"00AC"}} + * {"result":"BAD_REQUEST","error":"Path /3/1. Object must be Multiple !"} */ - + @Test + public void testCreateObjectInstanceWithInstanceIdMandatorySingleObjectById_Result_BAD_REQUEST() throws Exception { + String expectedPath = objectIdVer_3 + "/" + objectInstanceId_1; + String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}}"; + String actualResult = sendRPCreateById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expected = "Path " + expectedPath + ". Object must be Multiple !"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } /** * failed: cannot_create_instance_of_security_object - * Create {"id":"/0/2","value":{"0":"00AC"}} + * Create {"id":"/0/2","value":{"2":4}} + * {"result":"BAD_REQUEST","error":"Specified object id 0 absent in the list supported objects of the client or is security object!"} */ + @Test + public void testCreateObjectInstanceWithInstanceIdSecurityObjectById_Result_BAD_REQUEST() throws Exception { + String expectedPath = objectIdVer_0 + "/" + objectInstanceId_1; + String expectedValue = "{\"" + resourceId_0 + "\":{\"2\":4}}"; + String actualResult = sendRPCreateById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expectedObjectId = pathIdVerToObjectId((String) expectedPath); + LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); + String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } /** * failed: cannot_create_instance_of_absent_object - * Create {"id":"/50/2","value":{"0":"00AC"}} + * Create {"id":"/50/1","value":{"0":"00AC"}} + * {"result":"BAD_REQUEST","error":"Specified object id 50 absent in the list supported objects of the client or is security object!"} */ - + @Test + public void testCreateObjectInstanceWithInstanceIdAbsentObjectById_Result_BAD_REQUEST() throws Exception { + String expectedPath = objectIdVer_50+ "/" + objectInstanceId_1; + String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}}"; + String actualResult = sendRPCreateById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expectedObjectId = pathIdVerToObjectId((String) expectedPath); + LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); + String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } private String sendRPCreateById(String path, String value) throws Exception { String setRpcRequest = "{\"method\": \"Create\", \"params\": {\"id\": \"" + path + "\", \"value\": " + value + " }}"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java new file mode 100644 index 0000000000..6f24d7176c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; + + +public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTest { + + /** + * if there is such an instance + * Delete {"id":"/3303/12"} + * {"result":"DELETE"} + */ + @Test + public void testDeleteObjectInstanceIsSuchByIdKey_Result_DELETED() throws Exception { + String expectedPath = objectIdVer_3303 + "/" + objectInstanceId_12; + String actualResult = sendRPCDeleteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.DELETED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * if there is no such instance + * Delete {"id":"/19/12"} + * {"result":"NOT_FOUND"} + */ + @Test + public void testDeleteObjectInstanceIsNotSuchByIdKey_Result_NOT_FOUND() throws Exception { + String expectedPath = objectIdVer_19 + "/" + objectInstanceId_12; + String actualResult = sendRPCDeleteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); + } + + /** + * delete object + * Delete {"id":"/19_1.1"} + * {"result":"BAD_REQUEST","error":"Invalid path /19 : Only object instances can be delete"} + */ + @Test + public void testDeleteObjectByIdKey_Result_BAD_REQUEST() throws Exception { + String expectedPath = objectIdVer_19; + String actualResult = sendRPCDeleteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expected = "Invalid path " + pathIdVerToObjectId((String) expectedPath) + " : Only object instances can be delete"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + + /** + * delete resource + * Delete {"id":"/3/0/9"} + * {"result":"METHOD_NOT_ALLOWED"} + */ + @Test + public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception { + String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_9; + String actualResult = sendRPCDeleteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); + } + + + private String sendRPCDeleteById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Delete\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java index 64acf699ee..b8ca7be61f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java @@ -74,7 +74,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration expectedObjectIdVers.forEach(expected -> { try { String actualResult = sendDiscover((String) expected); - String expectedObjectId = objectIdVerToObjectId ((String) expected); + String expectedObjectId = pathIdVerToObjectId((String) expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String[] actualValues = rpcActualResult.get("value").asText().split(","); @@ -88,8 +88,12 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration /** * Discover {"id":"3/0"} - * - * @throws Exception + * If WriteAttributes not implemented: + * {"result":"CONTENT","value":",,,,,,,,,,,,,,,,,,,,,,,"} + * If WriteAttributes implemented and WriteAttributes saved + * Discover {"id":"19/0"} + * {"result":"CONTENT","value":"[;dim=2;pmin=10;pmax=60;gt=50;lt=42.2,;pmax=120, , , , , ;lt=45]"} */ @Test public void testDiscoverInstance_Return_CONTENT_LinksResourcesOnLyExpectedInstance() throws Exception { @@ -97,7 +101,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration String actualResult = sendDiscover(expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expectedObjectInstanceId = objectInstanceIdVerToObjectInstanceId (expected); + String expectedObjectInstanceId = pathIdVerToObjectId(expected); String[] actualValues = rpcActualResult.get("value").asText().split(","); assertTrue(actualValues.length > 0); assertEquals(0, Arrays.stream(actualValues).filter(path -> !path.contains(expectedObjectInstanceId)).collect(Collectors.toList()).size()); @@ -105,13 +109,20 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration /** * Discover {"id":"3/0/14"} - * - * @throws Exception + * If WriteAttributes implemented: + * {"result":"CONTENT","value":";pmax=100, "pmin":10, "ver"=1.0"} + * If WriteAttributes not implemented: + * {"result":"CONTENT","value":""} + * Discover {"id":"19_1.1/0/0"} + * If WriteAttributes implemented: + * {"result":"CONTENT","value":";pmax=100, "pmin":10, "ver"=1.1"} + * If WriteAttributes not implemented: + * {"result":"CONTENT","value":""} */ @Test public void testDiscoverResource_Return_CONTENT_LinksResourceOnLyExpectedResource() throws Exception { String expectedInstance = (String) expectedInstances.stream().findFirst().get(); - String expectedObjectInstanceId = objectInstanceIdVerToObjectInstanceId (expectedInstance); + String expectedObjectInstanceId = pathIdVerToObjectId(expectedInstance); LwM2mPath expectedPath = new LwM2mPath(expectedObjectInstanceId); int expectedResource = client.getClient().getObjectTree().getObjectEnablers().get(expectedPath.getObjectId()).getObjectModel().resources.entrySet().stream().findAny().get().getKey(); String expected = expectedInstance + "/" + expectedResource; @@ -125,12 +136,11 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration } /** - * Discover {"id":"2/0/2"} - * - * @throws Exception + * Discover {"id":"2/0"} + *{"result":"NOT_FOUND"} */ @Test - public void testDiscoverInstanceAbsentInObject_Return_NOT_FOUND() throws Exception { + public void testDiscoverObjectInstanceAbsentInObject_Return_NOT_FOUND() throws Exception { String expected = objectIdVer_2 + "/" + objectInstanceId_0; String actualResult = sendDiscover(expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -138,8 +148,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration } /** * Discover {"id":"2/0/2"} - * - * @throws Exception + * {"result":"NOT_FOUND"} */ @Test public void testDiscoverResourceAbsentInObject_Return_NOT_FOUND() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java new file mode 100644 index 0000000000..73ea0fb74c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java @@ -0,0 +1,186 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_4; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_8; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; + + +public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationTest { + + + /** + * Update FW + * Execute {"id":"5/0/2"} + * {"result":"CHANGED"} + */ + @Test + public void testExecuteUpdateFWById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_5 + "/" + resourceId_2; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * Update SW + * Execute {"id":"9/0/4"} + * {"result":"CHANGED"} + */ + @Test + public void testExecuteUpdateSWById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_9 + "/" + resourceId_4; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * Reboot + * Execute {"id":"3/0/4"} + * {"result":"CHANGED"} + */ + @Test + public void testExecuteRebootById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_4; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * Registration Update Trigger + * Execute {"id":"1/0/8"} + * {"result":"CHANGED"} + */ + @Test + public void testExecuteRegistrationUpdateTriggerById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_1 + "/" + resourceId_8; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + } + + + /** + * execute_resource_with_parameters (execute reboot after 60 seconds on device) + * Execute {"id":"3/0/4","value":60} + * {"result":"CHANGED"} + */ + @Test + public void testExecuteResourceWithParametersById_Result_CHANGED() throws Exception { + String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_4; + Object expectedValue = 60; + String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + } + + /** + * Bootstrap-Request Trigger + * Execute {"id":"1/0/9"} + * {"result":"BAD_REQUEST","error":"probably no bootstrap server configured"} + */ + @Test + public void testExecuteBootstrapRequestTriggerById_Result_BAD_REQUEST_Error_NoBootstrapServerConfigured() throws Exception { + String expectedPath = objectInstanceIdVer_1 + "/" + resourceId_9; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expected = "probably no bootstrap server configured"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + /** + * bad: resource operation not "E" + * Execute {"id":"5/0/3"} + * {"result":"BAD_REQUEST","error":"Resource with /5/0/3 is not executable."} + */ + @Test + public void testExecuteResourceWithOperationNotExecuteById_Result_METHOD_NOT_ALLOWED() throws Exception { + String expectedPath = objectInstanceIdVer_5 + "/" + resourceId_3; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expectedObjectId = pathIdVerToObjectId((String) expectedPath); + String expected = "Resource with " + expectedObjectId + " is not executable."; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + /** + * bad: execute_non_existing_resource_on_non_existing_object + * Execute {"id":"50/0/3"} + * {"result":"BAD_REQUEST","error":"Specified object id 50 absent in the list supported objects of the client or is security object!"} + */ + @Test + public void testExecuteNonExistingResourceOnNonExistingObjectById_Result_BAD_REQUEST() throws Exception { + String expectedPath = objectIdVer_50 + "/" + objectInstanceId_0 + "/" + resourceId_3; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expectedObjectId = pathIdVerToObjectId((String) expectedPath); + LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); + String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + /** + * bad: execute security object + * Execute {"id":"0/0/3"} + * {"result":"BAD_REQUEST","error":"Specified object id 0 absent in the list supported objects of the client or is security object!"} + */ + @Test + public void testExecuteSecurityObjectById_Result_NOT_FOUND() throws Exception { + String expectedPath = objectIdVer_0 + "/" + objectInstanceId_0 + "/" + resourceId_3; + String actualResult = sendRPCExecuteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expectedObjectId = pathIdVerToObjectId((String) expectedPath); + LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); + String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + + private String sendRPCExecuteById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Execute\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + + private String sendRPCExecuteWithValueById(String path, Object value) throws Exception { + String setRpcRequest = "{\"method\": \"Execute\", \"params\": {\"id\": \"" + path + "\", \"value\": " + value + " }}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java index 4f696e1c3c..9116659bc3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -52,7 +52,7 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest expectedObjectIdVers.forEach(expected -> { try { String actualResult = sendRPCById((String) expected); - String expectedObjectId = objectIdVerToObjectId ((String) expected); + String expectedObjectId = pathIdVerToObjectId((String) expected); LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); @@ -77,7 +77,7 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest expectedObjectIdVerInstances.forEach(expected -> { try { String actualResult = sendRPCById((String) expected); - String expectedObjectId = objectInstanceIdVerToObjectInstanceId ((String) expected); + String expectedObjectId = pathIdVerToObjectId((String) expected); LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); @@ -136,10 +136,10 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest @Test public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectIsLwM2mSingleResourceIsLwM2mMultipleResource() throws Exception { String expectedIdVer_1 = (String) expectedObjectIdVers.stream().filter(path -> (!((String)path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String)path).contains("/" + SERVER))).findFirst().get(); - String objectId_1 = objectIdVerToObjectId(expectedIdVer_1); + String objectId_1 = pathIdVerToObjectId(expectedIdVer_1); String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + resourceId_1; String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + resourceId_11; - String objectInstanceId_3 = objectIdVerToObjectId(objectInstanceIdVer_3); + String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3); String expectedIds = "[\"" + expectedIdVer_1 + "\", \"" + expectedIdVer3_0_1 + "\", \"" + expectedIdVer3_0_11 + "\"]"; String actualResult = sendCompositeRPCByIds(expectedIds); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -165,10 +165,10 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest String actualResult = sendCompositeRPCByIds(expectedIds); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String objectInstanceId_3 = objectInstanceIdVerToObjectInstanceId(objectInstanceIdVer_3); + String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3); LwM2mPath path = new LwM2mPath(objectInstanceId_3); String expected3_0 = objectInstanceId_3 + "=LwM2mObjectInstance [id=" + path.getObjectInstanceId() + ", resources={"; - String objectInstanceId_1 = objectInstanceIdVerToObjectInstanceId(objectInstanceIdVer_1); + String objectInstanceId_1 = pathIdVerToObjectId(objectInstanceIdVer_1); String expected1_0_1 = objectInstanceId_1 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value="; String expected1_0_2 = objectInstanceId_1 + "/" + resourceId_2 + "=null"; String actualValues = rpcActualResult.get("value").asText(); @@ -190,8 +190,8 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest String actualResult = sendCompositeRPCByKeys(expectedKeys); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String objectInstanceId_3 = objectInstanceIdVerToObjectInstanceId(objectInstanceIdVer_3); - String objectId_19 = objectIdVerToObjectId(objectIdVer_19); + String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3); + String objectId_19 = pathIdVerToObjectId(objectIdVer_19); String expected3_0_9 = objectInstanceId_3 + "/" + resourceId_9 + "=LwM2mSingleResource [id=" + resourceId_9 + ", value="; String expected3_0_14 = objectInstanceId_3 + "/" + resourceId_14 + "=LwM2mSingleResource [id=" + resourceId_14 + ", value="; String expected19_0_0 = objectId_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "=null"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java new file mode 100644 index 0000000000..ea23ab48b9 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_4; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_8; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; + + +public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MIntegrationTest { + + /** + * WriteAttributes {"id":"/3/0/14","attributes":{"pmax":100, "pmin":10}} + * if not implemented: + * {"result":"INTERNAL_SERVER_ERROR","error":"not implemented"} + * if implemented: + * {"result":"CHANGED"} + */ + @Test + public void testWriteAttributesResourceWithParametersById_Result_INTERNAL_SERVER_ERROR() throws Exception { + String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_14; + String expectedValue = "{\"pmax\":100, \"pmin\":10}"; + String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.INTERNAL_SERVER_ERROR.getName(), rpcActualResult.get("result").asText()); + String expected = "not implemented"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + private String sendRPCExecuteWithValueById(String path, String value) throws Exception { + String setRpcRequest = "{\"method\": \"WriteAttributes\", \"params\": {\"id\": \"" + path + "\", \"attributes\": " + value + " }}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java index 0150572351..3bc9c075f1 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql; import com.fasterxml.jackson.databind.node.ObjectNode; import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mPath; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; @@ -27,18 +28,21 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_2; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_15; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceInstanceId_2; public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTest { /** - * id + * update SingleResource: * WriteReplace {"id":"3/0/14","value":"+12"} + * {"result":"CHANGED"} */ @Test public void testWriteReplaceValueSingleResourceById_Result_CHANGED() throws Exception { @@ -57,6 +61,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** * key * WriteReplace {"key":"timezone","value":"+10"} + * {"result":"CHANGED"} */ @Test public void testWriteReplaceValueSingleResourceByKey_Result_CHANGED() throws Exception { @@ -76,6 +81,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** * id * WriteReplace {"id": "/19_1.1/0/0","value": {"0":"0000ad45675600", "15":"1525ad45675600cdef"}} + * {"result":"CHANGED"} */ @Test public void testWriteReplaceValueMultipleResource_Result_CHANGED_Value_Multi_Instance_Resource_must_in_Json_format() throws Exception { @@ -121,7 +127,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** - * bad - only read + * bad: singleResource, operation="R" - only read * WriteReplace {"id":"/3/0/9","value":90} * {"result":"METHOD_NOT_ALLOWED"} */ @@ -137,7 +143,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** * ids * WriteUpdate {"id":"/3/0","value":{"14":"+5","15":"Kiyv/Europe"}} - * + * {"result":"CHANGED"} */ @Test public void testWriteUpdateValueSingleResourceById_Result_CHANGED() throws Exception { @@ -165,6 +171,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** * id * WriteUpdate {"id": "/19_1.1/0","value": {"0":{"0":"00ad456756", "25":"25ad456756"}}} + * {"result":"CHANGED"} */ @Test public void testWriteUpdateValueMultipleResourceById_Result_CHANGED() throws Exception { @@ -194,6 +201,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** * ResourceInstance + KeySingleResource + IdSingleResource * WriteComposite {"nodes":{"/19/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}} + * {"result":"CHANGED"} */ @Test public void testWriteCompositeValueSingleResourceResourceInstanceByIdKey_Result_CHANGED() throws Exception { @@ -246,15 +254,17 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes /** - * create_2_instances_in_object - * new ObjectInstance if Object is Multiple & Resource Single - * Create {"id":"/19/2","value":{"1":2}} - * Create {"id":"/19/3","value":{"0":{"0":"00AC", "1":"ddff12"}}} + * update_resourceInstances&update_singleResource + * new ResourceInstance if Resource is Multiple & Resource Single + * - WriteReplace {"id":"/19_1.2/1/0","value":{"2":ddff12"}} + * - WriteReplace {"key":"UtfOffset","value":"+04"} + * - WriteReplace {"id":"/3/0/15","value":"Kiyv/Europe"} + * WriteComposite {"nodes":{"/19_1.1/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}}} + * {"result":"CHANGED"} */ @Test - public void testCreateObjectInstanceSingleByIdKey_Result_CHANGED() throws Exception { - int resourceInstanceId2 = 2; - String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId2; + public void testWriteCompositeCreateResourceInstanceUpdateSingleResourceByIdKey_Result_CHANGED() throws Exception { + String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId_2; String expectedValue19_1_0_2 = "00001234"; String expectedKey3_0_14 = resourceIdName_3_14; String expectedValue3_0_14 = "+04"; @@ -268,7 +278,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes actualResult = sendRPCReadById(expectedPath19_1_0_2); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String actualValues = rpcActualResult.get("value").asText(); - String expected = "LwM2mResourceInstance [id=" + resourceInstanceId2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId_2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadByKey(expectedKey3_0_14); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -283,20 +293,33 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes } /** - * failed: cannot_create_mandatory_single_object - * Create {"id":"/3/2","value":{"0":"00AC"}} - */ - - - /** - * failed: cannot_create_instance_of_security_object - * Create {"id":"/0/2","value":{"0":"00AC"}} - */ - - /** - * failed: cannot_create_instance_of_absent_object - * Create {"id":"/50/2","value":{"0":"00AC"}} + * composite_not created_new_instance... + * new ObjectInstance if Object is Multiple - bad + * - WriteReplace {"id":"/19_1.2/2/0","value":{"2":ddff12"}} + * - WriteReplace {"key":"UtfOffset","value":"+04"} + * - WriteReplace {"id":"/3/0/15","value":"Kiyv/Europe"} + * WriteComposite {"nodes":{"/19_1.1/1/0/2":"00001234", "UtfOffset":"+04", "/3/0/15":"Kiyv/Europe"}}} + * {"result":"BAD_REQUEST","error":"object instance /19/2 not found"} */ + @Test + public void testWriteCompositeCreateObjectInstanceUpdateSingleResourceByIdKey_Result_BAD_REQUEST() throws Exception { + String expectedPath19_1_2_2 = objectIdVer_19 + "/" + objectInstanceId_2 + "/" + resourceId_0 + "/" + resourceInstanceId_2; + String expectedValue19_1_0_2 = "00001234"; + String expectedKey3_0_14 = resourceIdName_3_14; + String expectedValue3_0_14 = "+04"; + String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedValue3_0_15 = "Kiyv/Europe"; + String nodes = "{\"" + expectedPath19_1_2_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + + "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; + String actualResult = sendCompositeRPC(nodes); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expectedObjectId = pathIdVerToObjectId((String) expectedPath19_1_2_2); + LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); + String expected = "object instance " + "/" + expectedPathId.getObjectId() + "/" + expectedPathId.getObjectInstanceId() + " not found"; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } private String sendRPCWriteStringById(String method, String path, String value) throws Exception { String setRpcRequest = "{\"method\": \"" + method + "\", \"params\": {\"id\": \"" + path + "\", \"value\": \"" + value + "\" }}"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java index ffaeb3f5c6..96006cbe67 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; @@ -40,6 +41,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.hasSize; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; @@ -133,7 +137,7 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { "}"; @Test - public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception { + public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile() throws Exception { String endpoint = "WithoutFirmwareInfoDevice"; createDeviceProfile(TRANSPORT_CONFIGURATION); NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java index 4722246b4f..cf841d6204 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java @@ -33,6 +33,4 @@ public class OtherConfiguration extends PowerSavingConfiguration { private Long pagingTransmissionWindow; private String fwUpdateResource; private String swUpdateResource; - private boolean compositeOperationsSupport; - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index ff2757ba3b..8106871aee 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -363,7 +363,7 @@ public class LwM2mClient implements Serializable { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); if (verSupportedObject == null) { - return String.format("Specified resource id %s is missing from the client!", path); + return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId()); } else { String verRez = getVerFromPathIdVerOrId(path); if ((verRez != null && !verRez.equals(verSupportedObject)) || From 0c5d07e3a7f160e42497f71a2be3dc2a0cdeab24 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 3 Sep 2021 18:37:13 +0300 Subject: [PATCH 05/21] lwm2m: if client reboot or new connect and sharedAttribute not change: log "warn" ...Value is not changed or bad --- .../DefaultLwM2MAttributesService.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index f70af7bf0e..1da35484af 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -58,6 +58,7 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromJson; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; @@ -241,32 +242,30 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { private void pushUpdateMultiToClientIfNeeded(LwM2mClient client, ResourceModel resourceModel, JsonElement newValProto, Map valueOld, String versionedId) { - Map newValues = convertMultiResourceValuesFromJson((JsonObject)newValProto, resourceModel.type, versionedId); - if (newValues.size() > 0) { - if (valueOld != null && valueOld.size() > 0) { - valueOld.values().stream().forEach((v) -> { - if (newValues.containsKey(v.getId())) { - boolean isValueEquals; - if (OPAQUE.equals(v.getType())) { - isValueEquals = Arrays.equals((byte [])newValues.get(v.getId()), (byte[]) v.getValue()); - } else { - isValueEquals = newValues.get(v.getId()).toString().equals(v.getValue().toString()); - } - if (isValueEquals) { - newValues.remove(v.getId()); - } + Map newValues = convertMultiResourceValuesFromJson((JsonObject) newValProto, resourceModel.type, versionedId); + if (newValues.size() > 0 && valueOld != null && valueOld.size() > 0) { + valueOld.values().stream().forEach((v) -> { + if (newValues.containsKey(v.getId())) { + boolean isValueEquals; + if (OPAQUE.equals(v.getType())) { + isValueEquals = Arrays.equals((byte[]) newValues.get(v.getId()), (byte[]) v.getValue()); + } else { + isValueEquals = newValues.get(v.getId()).toString().equals(v.getValue().toString()); } - }); - } + if (isValueEquals) { + newValues.remove(v.getId()); + } + } + }); } if (newValues.size() > 0) { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValues).timeout(this.config.getTimeout()).build(); downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId)); } else { - log.error("Failed update resource [{}] [{}]", versionedId, newValProto); + log.warn("Failed update resource [{}] [{}]", versionedId, newValProto); String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", - LOG_LWM2M_ERROR, versionedId, newValProto); + LOG_LWM2M_WARN, versionedId, newValProto); logService.log(client, logMsg); } } From e68a713b4fc3fdf1163d3287e649a48f757a72d7 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 6 Sep 2021 11:18:27 +0300 Subject: [PATCH 06/21] lwm2m: if client reboot or new connect and sharedAttribute not change: log "warn" ...Value is not changed "error" ...Value is bad --- .../attributes/DefaultLwM2MAttributesService.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index 1da35484af..8752657f94 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -226,13 +226,13 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. New value is bad", LOG_LWM2M_ERROR, versionedId, "null"); logService.log(lwM2MClient, logMsg); - log.error("Failed update resource [{}] [{}]", versionedId, newValue); - } else if ((valueOld != null && newValue.toString().equals(valueOld.toString()))){ + log.error("Failed update resource [{}] [{}]", versionedId, "null"); + } else if (newValue.toString().equals(valueOld.toString())){ if (logFailedUpdateOfNonChangedValue) { - String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed", - LOG_LWM2M_INFO, versionedId, newValue); + String logMsg = String.format("%s: Didn't update the versionedId resource - %s value - %s. Value is not changed", + LOG_LWM2M_WARN, versionedId, newValue); logService.log(lwM2MClient, logMsg); - log.info("Failed update resource [{}] [{}]. Value is not changed", versionedId, newValue); + log.warn("Didn't update resource [{}] [{}]. Value is not changed", versionedId, newValue); } } else { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); @@ -263,8 +263,8 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValues).timeout(this.config.getTimeout()).build(); downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId)); } else { - log.warn("Failed update resource [{}] [{}]", versionedId, newValProto); - String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", + log.warn("Didn't update resource [{}] [{}]", versionedId, newValProto); + String logMsg = String.format("%s: Didn't update resource versionedId - %s value - %s. Value is not changed", LOG_LWM2M_WARN, versionedId, newValProto); logService.log(client, logMsg); } From be5ec4fce93e3138d28c25ff069d47f45e72af4c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 9 Aug 2021 15:10:04 +0300 Subject: [PATCH 07/21] Lwm2m: Device validate securityKey --- .../server/controller/DeviceController.java | 1 + .../LwM2MServerSecurityInfoRepository.java | 106 ++++++++++++++++-- .../data/security/DeviceCredentials.java | 8 ++ 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 05385ee948..f1a58e7022 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -291,6 +291,7 @@ public class DeviceController extends BaseController { public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException { checkNotNull(deviceCredentials); try { + lwM2MServerSecurityInfoRepository.verifySecurityKeyDevice(deviceCredentials); Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null); diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java index b4ac3d30fd..f8ff9a687a 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java @@ -16,33 +16,29 @@ package org.thingsboard.server.service.lwm2m; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.util.Hex; +import org.eclipse.leshan.core.util.SecurityUtil; +import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; +import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import java.math.BigInteger; -import java.security.AlgorithmParameters; +import java.io.IOException; import java.security.GeneralSecurityException; -import java.security.KeyFactory; import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateEncodingException; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.KeySpec; @Slf4j @Service @@ -84,5 +80,91 @@ public class LwM2MServerSecurityInfoRepository { return ""; } + public void verifySecurityKeyDevice(DeviceCredentials result) throws InvalidConfigurationException, JsonProcessingException { + ObjectNode node = result.getNodeCredentialsValue(); + checkClientKey ((ObjectNode) node.get("client")); + checkServerKey ((ObjectNode) node.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); + checkServerKey ((ObjectNode) node.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); + } + + private void checkClientKey (ObjectNode node) throws InvalidConfigurationException { + String modeName = node.get("securityConfigClientMode").asText(); + // checks security config + String value = node.get("key").textValue(); + if (SecurityMode.RPK.name().equals(modeName)) { + assertIf(decodeRfc7250PublicKey(Hex.decodeHex(((String) value).toCharArray())) == null, + "raw-public-key mode, Client`s public key or id must be RFC7250 encoded public key"); + } else if (SecurityMode.X509.name().equals(modeName)) { + assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, + "x509 mode, Client`s public key must be DER encoded X.509 certificate"); + } + + } + + private void checkServerKey (ObjectNode node, String serverType) throws InvalidConfigurationException { + String modeName = node.get("securityMode").asText(); + // checks security config + if (SecurityMode.RPK.name().equals(modeName)) { + checkRPKServer(node, serverType); + } else if (SecurityMode.X509.name().equals(modeName)) { + checkX509Server(node); + } + + } + + protected void checkRPKServer(ObjectNode node, String serverType) throws InvalidConfigurationException { + String value = node.get("clientSecretKey").textValue(); + assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(((String) value).toCharArray())) == null, + "raw-public-key mode, " + serverType + " secret key must be RFC5958 encoded private key"); + value = node.get("clientPublicKeyOrId").textValue(); + assertIf(decodeRfc7250PublicKey(Hex.decodeHex(((String) value).toCharArray())) == null, + "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); + } + + protected void checkX509Server(ObjectNode node) throws InvalidConfigurationException { + String value = node.get("clientSecretKey").textValue();; + assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(((String) value).toCharArray())) == null, + "x509 mode, secret key must be RFC5958 encoded private key"); + value = node.get("clientPublicKeyOrId").textValue(); + assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, + "x509 mode, server public key must be DER encoded X.509 certificate"); + + } + + protected PrivateKey decodeRfc5958PrivateKey(byte[] encodedKey) throws InvalidConfigurationException { + try { + return SecurityUtil.privateKey.decode(encodedKey); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + protected PublicKey decodeRfc7250PublicKey(byte[] encodedKey) throws InvalidConfigurationException { + try { + return SecurityUtil.publicKey.decode(encodedKey); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + protected Certificate decodeCertificate(byte[] encodedCert) throws InvalidConfigurationException { + try { + return SecurityUtil.certificate.decode(encodedCert); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + protected static void assertIf(boolean condition, String message) throws InvalidConfigurationException { + if (condition) { + throw new InvalidConfigurationException(message); + } + } + + protected static boolean isEmpty(byte[] array) { + return array == null || array.length == 0; + } + + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java index 337e6cf349..e7c5d7d19b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.security; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.DeviceCredentialsId; @@ -87,4 +90,9 @@ public class DeviceCredentials extends BaseData implements + id + "]"; } + public ObjectNode getNodeCredentialsValue () throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return (ObjectNode) mapper.readTree(this.credentialsValue); + } + } From 4b74d2bde1c8ad6e7354288b8a907c96974bf905 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 7 Sep 2021 23:16:18 +0300 Subject: [PATCH 08/21] lwm2m: fix bug WriteUpdate & updateAttributes after sleep --- .../DefaultLwM2MAttributesService.java | 23 +++--- .../DefaultLwM2mDownlinkMsgHandler.java | 72 ++++++++++++------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index 8752657f94..38b4790216 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -57,7 +57,6 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromJson; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; @@ -212,7 +211,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { Object newValProto = getValueFromKvProto(tsKvProto.getKv()); Object oldResourceValue = this.getResourceValueFormatKv(lwM2MClient, pathIdVer); if (!resourceModel.multiple || !(newValProto instanceof JsonElement)) { - this.pushUpdateToClientIfNeeded(lwM2MClient, oldResourceValue, newValProto, pathIdVer, logFailedUpdateOfNonChangedValue); + this.pushUpdateToClientIfNeeded(lwM2MClient, oldResourceValue, newValProto, pathIdVer, logFailedUpdateOfNonChangedValue, resourceModel.type); } else { pushUpdateMultiToClientIfNeeded(lwM2MClient, resourceModel, (JsonElement) newValProto, (Map) oldResourceValue, pathIdVer); @@ -221,22 +220,22 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { } private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, - String versionedId, boolean logFailedUpdateOfNonChangedValue) { + String versionedId, boolean logFailedUpdateOfNonChangedValue, ResourceModel.Type type) { if (newValue == null) { String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. New value is bad", LOG_LWM2M_ERROR, versionedId, "null"); logService.log(lwM2MClient, logMsg); - log.error("Failed update resource [{}] [{}]", versionedId, "null"); - } else if (newValue.toString().equals(valueOld.toString())){ - if (logFailedUpdateOfNonChangedValue) { - String logMsg = String.format("%s: Didn't update the versionedId resource - %s value - %s. Value is not changed", - LOG_LWM2M_WARN, versionedId, newValue); - logService.log(lwM2MClient, logMsg); - log.warn("Didn't update resource [{}] [{}]. Value is not changed", versionedId, newValue); - } - } else { + log.error("Failed update resource [{}] [{}]", versionedId, "null"); + } else if ((valueOld == null) || + ((OPAQUE.equals(type) && !Arrays.equals((byte[]) newValue, (byte[]) valueOld)) || + !newValue.equals(valueOld))) { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, lwM2MClient, versionedId)); + } else if (logFailedUpdateOfNonChangedValue) { + String logMsg = String.format("%s: Didn't update the versionedId resource - %s value - %s. Value is not changed", + LOG_LWM2M_WARN, versionedId, newValue); + logService.log(lwM2MClient, logMsg); + log.warn("Didn't update resource [{}] [{}]. Value is not changed", versionedId, newValue); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 2ef832333b..edf1d65fa0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -345,39 +345,57 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im @Override public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback) { try { - validateVersionedId(client, request); - WriteRequest downlink = null; + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); - ContentFormat contentFormat = getWriteRequestContentFormat(client, request, this.config.getModelProvider()); - if (resultIds.isObjectInstance()) { - /* - * params = "{\"id\":0,\"value\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" - * int rscId = resultIds.getObjectInstanceId(); - * contentFormat – Format of the payload (TLV or JSON). - */ - Collection resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); - if (resources.size() > 0) { - downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); - } else { - callback.onValidationError(toString(request), "No resources to update!"); - } - } else if (resultIds.isResource()) { + if (resultIds.isObjectInstance() || resultIds.isResource()) { + validateVersionedId(client, request); ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); - if (resourceModelWrite.multiple) { - if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) { - Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId()); - downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), - value, resourceModelWrite.type); + if (resourceModelWrite != null) { + WriteRequest downlink = null; + ContentFormat contentFormat = getWriteRequestContentFormat(client, request, this.config.getModelProvider()); + if (resultIds.isObjectInstance()) { + /* + * params = "{\"id\":0,\"value\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" + * int rscId = resultIds.getObjectInstanceId(); + * contentFormat – Format of the payload (TLV or JSON). + */ + Collection resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); + if (resources.size() > 0) { + downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); + } else { + callback.onValidationError(toString(request), "No resources to update!"); + } + } else if (resultIds.isResource()) { + if (resourceModelWrite.multiple) { + if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) { + Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId()); + downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), + value, resourceModelWrite.type); + } else { + callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!"); + } + } + } + if (downlink != null) { + sendSimpleRequest(client, downlink, request.getTimeout(), callback); } else { - callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!"); + callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Multi-Instance Resource !"); } } + else { + callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!"); + } } - if (downlink != null) { - sendSimpleRequest(client, downlink, request.getTimeout(), callback); - } else { - callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Multi-Instance Resource !"); + else { + callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Resource (multiple)"); } + + + + + + + } catch (Exception e) { callback.onValidationError(toString(request), e.getMessage()); } @@ -585,7 +603,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im } } else if (pathIds.isResource()) { ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider); - if (!resourceModel.multiple) { + if (resourceModel!= null && !resourceModel.multiple) { if (OBJLNK.equals(resourceModel.type)) { return ContentFormat.LINK; } else if (OPAQUE.equals(resourceModel.type)) { From e4941fdd103ef2c148c44b4f7ed0b138fc801c2c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 9 Aug 2021 18:00:08 +0300 Subject: [PATCH 09/21] Lwm2m: DeviceProfile validate securityKey --- .../controller/DeviceProfileController.java | 2 +- .../LwM2MServerSecurityInfoRepository.java | 62 +++++++++++++------ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 3a68551773..46d76299ec 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -157,7 +157,7 @@ public class DeviceProfileController extends BaseController { isSoftwareChanged = true; } } - + lwM2MServerSecurityInfoRepository.verifySecurityKeyDeviceProfile(deviceProfile); DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java index f8ff9a687a..cb7831e2ce 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java @@ -26,6 +26,8 @@ import org.eclipse.leshan.core.util.SecurityUtil; import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; @@ -39,6 +41,7 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.util.Map; @Slf4j @Service @@ -80,23 +83,27 @@ public class LwM2MServerSecurityInfoRepository { return ""; } - public void verifySecurityKeyDevice(DeviceCredentials result) throws InvalidConfigurationException, JsonProcessingException { - ObjectNode node = result.getNodeCredentialsValue(); - checkClientKey ((ObjectNode) node.get("client")); - checkServerKey ((ObjectNode) node.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); - checkServerKey ((ObjectNode) node.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); + public void verifySecurityKeyDevice(DeviceCredentials deviceCredentials) throws InvalidConfigurationException, JsonProcessingException { + ObjectNode nodeCredentialsValue = deviceCredentials.getNodeCredentialsValue(); + checkClientKey ((ObjectNode) nodeCredentialsValue.get("client")); + checkServerKey ((ObjectNode) nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); + checkServerKey ((ObjectNode) nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); } private void checkClientKey (ObjectNode node) throws InvalidConfigurationException { String modeName = node.get("securityConfigClientMode").asText(); // checks security config - String value = node.get("key").textValue(); + if (SecurityMode.RPK.name().equals(modeName)) { + String value = node.get("key").textValue(); assertIf(decodeRfc7250PublicKey(Hex.decodeHex(((String) value).toCharArray())) == null, "raw-public-key mode, Client`s public key or id must be RFC7250 encoded public key"); } else if (SecurityMode.X509.name().equals(modeName)) { - assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, - "x509 mode, Client`s public key must be DER encoded X.509 certificate"); + String value = node.get("cert").textValue(); + if (value != null && !value.isEmpty()) { + assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, + "x509 mode, Client`s public key must be DER encoded X.509 certificate"); + } } } @@ -107,28 +114,47 @@ public class LwM2MServerSecurityInfoRepository { if (SecurityMode.RPK.name().equals(modeName)) { checkRPKServer(node, serverType); } else if (SecurityMode.X509.name().equals(modeName)) { - checkX509Server(node); + checkX509Server(node, serverType); } - } protected void checkRPKServer(ObjectNode node, String serverType) throws InvalidConfigurationException { String value = node.get("clientSecretKey").textValue(); - assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(((String) value).toCharArray())) == null, + assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(value.toCharArray())) == null, "raw-public-key mode, " + serverType + " secret key must be RFC5958 encoded private key"); value = node.get("clientPublicKeyOrId").textValue(); - assertIf(decodeRfc7250PublicKey(Hex.decodeHex(((String) value).toCharArray())) == null, + assertIf(decodeRfc7250PublicKey(Hex.decodeHex(value.toCharArray())) == null, "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); } - protected void checkX509Server(ObjectNode node) throws InvalidConfigurationException { - String value = node.get("clientSecretKey").textValue();; - assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(((String) value).toCharArray())) == null, - "x509 mode, secret key must be RFC5958 encoded private key"); + protected void checkX509Server(ObjectNode node, String serverType) throws InvalidConfigurationException { + String value = node.get("clientSecretKey").textValue(); + assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(value.toCharArray())) == null, + "x509 mode " + serverType + " secret key must be RFC5958 encoded private key"); value = node.get("clientPublicKeyOrId").textValue(); - assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, - "x509 mode, server public key must be DER encoded X.509 certificate"); + assertIf(decodeCertificate(Hex.decodeHex(value.toCharArray())) == null, + "x509 mode " + serverType + " public key must be DER encoded X.509 certificate"); + + } + public void verifySecurityKeyDeviceProfile(DeviceProfile deviceProfile) throws InvalidConfigurationException, JsonProcessingException { + Map serverBs = ((Lwm2mDeviceProfileTransportConfiguration)deviceProfile.getProfileData().getTransportConfiguration()).getBootstrap().getBootstrapServer(); + checkDeviceProfileServer (serverBs, "Servers: BootstrapServer`s"); + Map serverLwm2m = ((Lwm2mDeviceProfileTransportConfiguration)deviceProfile.getProfileData().getTransportConfiguration()).getBootstrap().getLwm2mServer(); + checkDeviceProfileServer (serverLwm2m, "Servers: Lwm2mServer`s"); + + } + + protected void checkDeviceProfileServer (Map server, String serverType) throws InvalidConfigurationException{ + // checks security config + String value = (String) server.get("serverPublicKey"); + if (SecurityMode.RPK.name().equals(server.get("securityMode"))) { + assertIf(decodeRfc7250PublicKey(Hex.decodeHex(value.toCharArray())) == null, + "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); + } else if (SecurityMode.X509.name().equals(server.get("securityMode"))) { + assertIf(decodeCertificate(Hex.decodeHex(value.toCharArray())) == null, + "x509 mode, " + serverType + " public key must be DER encoded X.509 certificate"); + } } protected PrivateKey decodeRfc5958PrivateKey(byte[] encodedKey) throws InvalidConfigurationException { From b40d0a7ac2ebe85dda4e4df7a234ba9eb0c9424d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 10 Aug 2021 16:58:32 +0300 Subject: [PATCH 10/21] Lwm2m: DeviceProfile validate securityKey JsonNode --- .../LwM2MServerSecurityInfoRepository.java | 18 +++++++++--------- .../data/security/DeviceCredentials.java | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java index cb7831e2ce..8750fa466c 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java @@ -17,7 +17,7 @@ package org.thingsboard.server.service.lwm2m; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.SecurityMode; @@ -84,13 +84,13 @@ public class LwM2MServerSecurityInfoRepository { } public void verifySecurityKeyDevice(DeviceCredentials deviceCredentials) throws InvalidConfigurationException, JsonProcessingException { - ObjectNode nodeCredentialsValue = deviceCredentials.getNodeCredentialsValue(); - checkClientKey ((ObjectNode) nodeCredentialsValue.get("client")); - checkServerKey ((ObjectNode) nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); - checkServerKey ((ObjectNode) nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); + JsonNode nodeCredentialsValue = deviceCredentials.getNodeCredentialsValue(); + checkClientKey (nodeCredentialsValue.get("client")); + checkServerKey (nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); + checkServerKey (nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); } - private void checkClientKey (ObjectNode node) throws InvalidConfigurationException { + private void checkClientKey (JsonNode node) throws InvalidConfigurationException { String modeName = node.get("securityConfigClientMode").asText(); // checks security config @@ -108,7 +108,7 @@ public class LwM2MServerSecurityInfoRepository { } - private void checkServerKey (ObjectNode node, String serverType) throws InvalidConfigurationException { + private void checkServerKey (JsonNode node, String serverType) throws InvalidConfigurationException { String modeName = node.get("securityMode").asText(); // checks security config if (SecurityMode.RPK.name().equals(modeName)) { @@ -118,7 +118,7 @@ public class LwM2MServerSecurityInfoRepository { } } - protected void checkRPKServer(ObjectNode node, String serverType) throws InvalidConfigurationException { + protected void checkRPKServer(JsonNode node, String serverType) throws InvalidConfigurationException { String value = node.get("clientSecretKey").textValue(); assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(value.toCharArray())) == null, "raw-public-key mode, " + serverType + " secret key must be RFC5958 encoded private key"); @@ -127,7 +127,7 @@ public class LwM2MServerSecurityInfoRepository { "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); } - protected void checkX509Server(ObjectNode node, String serverType) throws InvalidConfigurationException { + protected void checkX509Server(JsonNode node, String serverType) throws InvalidConfigurationException { String value = node.get("clientSecretKey").textValue(); assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(value.toCharArray())) == null, "x509 mode " + serverType + " secret key must be RFC5958 encoded private key"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java index e7c5d7d19b..d49272f2c6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java @@ -16,8 +16,8 @@ package org.thingsboard.server.common.data.security; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.DeviceCredentialsId; @@ -90,9 +90,9 @@ public class DeviceCredentials extends BaseData implements + id + "]"; } - public ObjectNode getNodeCredentialsValue () throws JsonProcessingException { + public JsonNode getNodeCredentialsValue () throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); - return (ObjectNode) mapper.readTree(this.credentialsValue); + return mapper.readTree(this.credentialsValue); } } From d491e0f664725404cec24f8ee4e835916b402b90 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 7 Sep 2021 23:16:18 +0300 Subject: [PATCH 11/21] lwm2m: Security key validate in dao --- .../server/controller/BaseController.java | 8 +- .../server/controller/DeviceController.java | 1 - .../controller/DeviceProfileController.java | 1 - .../server/controller/Lwm2mController.java | 2 +- .../server/service/lwm2m/LwM2MService.java | 24 +++ .../AbstractLwM2MIntegrationTest.java | 44 ++++- .../security/sql/PskLwm2mIntegrationTest.java | 2 +- .../dao/device/DeviceCredentialsService.java | 4 + .../data/security/DeviceCredentials.java | 6 - .../lwm2m/server/LwM2mServerListener.java | 6 +- .../DefaultLwM2mDownlinkMsgHandler.java | 38 ++-- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 2 - .../credentials/shell/lwM2M_credentials.sh | 1 - .../service/DefaultTransportService.java | 1 - dao/pom.xml | 4 + .../device/DeviceCredentialsServiceImpl.java | 170 ++++++++++++++++++ .../dao/device/DeviceProfileServiceImpl.java | 6 + .../credentials/shell/lwM2M_credentials.sh | 2 +- .../lwm2m-observe-attr-telemetry.component.ts | 8 +- 19 files changed, 280 insertions(+), 50 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index c37587f546..d456d7026b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -126,7 +126,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.edge.EdgeLicenseService; import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; -import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository; +import org.thingsboard.server.service.lwm2m.LwM2MServiceImpl; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.resource.TbResourceService; @@ -185,6 +185,9 @@ public abstract class BaseController { @Autowired protected DeviceProfileService deviceProfileService; + @Autowired + protected LwM2MServiceImpl lwM2MService; + @Autowired protected AssetService assetService; @@ -263,9 +266,6 @@ public abstract class BaseController { @Autowired protected TbDeviceProfileCache deviceProfileCache; - @Autowired - protected LwM2MServerSecurityInfoRepository lwM2MServerSecurityInfoRepository; - @Autowired(required = false) protected EdgeService edgeService; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index f1a58e7022..05385ee948 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -291,7 +291,6 @@ public class DeviceController extends BaseController { public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException { checkNotNull(deviceCredentials); try { - lwM2MServerSecurityInfoRepository.verifySecurityKeyDevice(deviceCredentials); Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null); diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 46d76299ec..4f74676be5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -157,7 +157,6 @@ public class DeviceProfileController extends BaseController { isSoftwareChanged = true; } } - lwM2MServerSecurityInfoRepository.verifySecurityKeyDeviceProfile(deviceProfile); DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 64537d9f5f..fb71d4ea91 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -46,7 +46,7 @@ public class Lwm2mController extends BaseController { @ResponseBody public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("isBootstrapServer") boolean bootstrapServer) throws ThingsboardException { try { - return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(bootstrapServer); + return lwM2MService.getServerSecurityInfo(bootstrapServer); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java new file mode 100644 index 0000000000..7d1a69d270 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.lwm2m; + +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; + +public interface LwM2MService { + + ServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer); + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java index 522abba65a..1bd9245cb2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java @@ -57,6 +57,9 @@ import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapServers; +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MServerBootstrap; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; @@ -83,9 +86,18 @@ import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST_BS; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT_BS; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT_BS; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID_BS; @DaoSqlTest public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -312,7 +324,13 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest LwM2MCredentials credentials = new LwM2MCredentials(); credentials.setClient(clientCredentials); - + // TODO + /** + * Without: credentials.setBootstrap(createBootstrapConfig()); + * doPost("/api/device/credentials", deviceCredentials).andExpect(status().isBadRequest()) + * .andExpect(statusReason(containsString("Device credentials are missing fields or mandatory value in these fields: bootstrap, bootstrapServer, lwm2mServer"))); + */ + credentials.setBootstrap(createBootstrapConfig()); deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); return device; @@ -351,6 +369,30 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); } + protected LwM2MBootstrapConfig createBootstrapConfig() { + LwM2MBootstrapConfig bootstrap = new LwM2MBootstrapConfig(); + LwM2MBootstrapServers servers = new LwM2MBootstrapServers(); + servers.setShortId(SHORT_SERVER_ID); + bootstrap.setServers(servers); + LwM2MServerBootstrap server = new LwM2MServerBootstrap(); + server.setHost(HOST); + server.setPort(PORT); + server.setSecurityHost(HOST); + server.setSecurityPort(SECURE_PORT); + server.setServerId(servers.getShortId()); + server.setBootstrapServerIs(false); + bootstrap.setLwm2mServer(server); + LwM2MServerBootstrap serverBS = new LwM2MServerBootstrap(); + serverBS.setHost(HOST_BS); + serverBS.setPort(PORT_BS); + serverBS.setSecurityHost(HOST_BS); + serverBS.setSecurityPort(SECURE_PORT_BS); + serverBS.setServerId(SHORT_SERVER_ID_BS); + serverBS.setBootstrapServerIs(true); + bootstrap.setBootstrapServer(serverBS); + return bootstrap; + } + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); postRequest.file(content); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index 014ab752fe..c99f978dae 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -33,7 +33,7 @@ public class PskLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest { @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { String pskIdentity = "SOME_PSK_ID"; - String pskKey = "73656372657450534b"; + String pskKey = "0123456789ABCDEF0123456789ABCDEF"; PSKClientCredentials clientCredentials = new PSKClientCredentials(); clientCredentials.setEndpoint(ENDPOINT); clientCredentials.setKey(pskKey); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java index e131064953..9a333058a2 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.dao.device; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; import org.thingsboard.server.common.data.security.DeviceCredentials; public interface DeviceCredentialsService { @@ -30,4 +32,6 @@ public interface DeviceCredentialsService { DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); + + void verifyLwm2mSecurityKeyDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) ; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java index d49272f2c6..4ccf07f2b0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java @@ -89,10 +89,4 @@ public class DeviceCredentials extends BaseData implements + credentialsId + ", credentialsValue=" + credentialsValue + ", createdTime=" + createdTime + ", id=" + id + "]"; } - - public JsonNode getNodeCredentialsValue () throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readTree(this.credentialsValue); - } - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 898a1ad8b2..edc408a78d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -40,7 +40,7 @@ public class LwM2mServerListener { public final RegistrationListener registrationListener = new RegistrationListener() { /** - * Register – запрос, представленный в виде POST /rd?… + * Register – query represented as POST /rd?… */ @Override public void registered(Registration registration, Registration previousReg, @@ -49,7 +49,7 @@ public class LwM2mServerListener { } /** - * Update – представляет из себя CoAP POST запрос на URL, полученный в ответ на Register. + * Update – query represented as CoAP POST request for the URL received in response to Register. */ @Override public void updated(RegistrationUpdate update, Registration updatedRegistration, @@ -58,7 +58,7 @@ public class LwM2mServerListener { } /** - * De-register (CoAP DELETE) – отправляется клиентом в случае инициирования процедуры выключения. + * De-register (CoAP DELETE) – Sent by the client when a shutdown procedure is initiated. */ @Override public void unregistered(Registration registration, Collection observations, boolean expired, diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index edf1d65fa0..b99fd2ac27 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -345,13 +345,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im @Override public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback) { try { - LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); if (resultIds.isObjectInstance() || resultIds.isResource()) { validateVersionedId(client, request); - ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); - if (resourceModelWrite != null) { - WriteRequest downlink = null; + WriteRequest downlink = null; ContentFormat contentFormat = getWriteRequestContentFormat(client, request, this.config.getModelProvider()); if (resultIds.isObjectInstance()) { /* @@ -365,15 +362,21 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im } else { callback.onValidationError(toString(request), "No resources to update!"); } - } else if (resultIds.isResource()) { - if (resourceModelWrite.multiple) { - if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) { - Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId()); - downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), - value, resourceModelWrite.type); - } else { - callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!"); + } + else if (resultIds.isResource()) { + ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); + if (resourceModelWrite != null) { + if (resourceModelWrite.multiple) { + if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) { + Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId()); + downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), + value, resourceModelWrite.type); + } else { + callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!"); + } } + } else { + callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!"); } } if (downlink != null) { @@ -381,21 +384,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im } else { callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Multi-Instance Resource !"); } - } - else { - callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!"); - } } else { callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Resource (multiple)"); } - - - - - - - } catch (Exception e) { callback.onValidationError(toString(request), e.getMessage()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 2c4f911db6..84ac0277a3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -418,8 +418,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl * #1 clientOnlyObserveAfterConnect == true * - Only Observe Request to the client marked as observe from the profile configuration. * #2. clientOnlyObserveAfterConnect == false - * После регистрации отправляю запрос на read всех ресурсов, которые после регистрации есть у клиента, - * а затем запрос на observe (edited) * - Read Request to the client after registration to read all resource values for all objects * - then Observe Request to the client marked as observe from the profile configuration. * diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh index dcf38b4f10..f68ca30005 100644 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh @@ -321,7 +321,6 @@ cert_end_point() { } if [ "$client_start" -lt "$client_finish" ]; then - echo "Файл содержит, как минимум, одно слово Bash." echo echo "==Start Client==" while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index bdc03d767c..dbe42cf9f4 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -899,7 +899,6 @@ public class DefaultTransportService implements TransportService { String resourceId = msg.getResourceKey(); transportResourceCache.update(tenantId, resourceType, resourceId); sessions.forEach((id, mdRez) -> { - log.warn("ResourceUpdate - [{}] [{}]", id, mdRez); transportCallbackExecutor.submit(() -> mdRez.getListener().onResourceUpdate(Optional.ofNullable(msg))); }); diff --git a/dao/pom.xml b/dao/pom.xml index 1ef95d8358..736ba64326 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -227,6 +227,10 @@ org.elasticsearch.client rest + + org.eclipse.leshan + leshan-server-cf + diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 3cce5e1506..9d0ff83491 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -15,9 +15,14 @@ */ package org.thingsboard.server.dao.device; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.eclipse.leshan.core.SecurityMode; +import org.eclipse.leshan.core.util.SecurityUtil; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; @@ -30,6 +35,7 @@ import org.thingsboard.server.common.data.device.credentials.BasicMqttCredential import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -39,10 +45,20 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateString; + @Service @Slf4j public class DeviceCredentialsServiceImpl extends AbstractEntityService implements DeviceCredentialsService { @@ -92,6 +108,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen break; case LWM2M_CREDENTIALS: formatSimpleLwm2mCredentials(deviceCredentials); + verifySecurityKeyDevice(deviceCredentials); break; } log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); @@ -237,4 +254,157 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen } }; + + private void verifySecurityKeyDevice(DeviceCredentials deviceCredentials) { + try { +// LwM2MCredentialsValid credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), LwM2MCredentialsValid.class); + JsonNode nodeCredentialsValue = JacksonUtil.toJsonNode(deviceCredentials.getCredentialsValue()); + String [] fields = {"client", "bootstrap", "bootstrap:bootstrapServer", "bootstrap:lwm2mServer"}; + String validateMsg = validateNodeCredentials (nodeCredentialsValue, fields); + if (validateMsg.isEmpty()) { + checkClientKey(nodeCredentialsValue.get("client")); + checkServerKey(nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); + checkServerKey(nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); + } + else { + throw new DataValidationException(validateMsg); + } + } catch (DataValidationException | DecoderException e) { + throw new DataValidationException(e.getMessage()); + } + } + + public void verifyLwm2mSecurityKeyDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) { + try { + Map serverBs = transportConfiguration.getBootstrap().getBootstrapServer(); + checkDeviceProfileServer (serverBs, "Servers: BootstrapServer`s"); + Map serverLwm2m = transportConfiguration.getBootstrap().getLwm2mServer(); + checkDeviceProfileServer (serverLwm2m, "Servers: Lwm2mServer`s"); + } catch (DataValidationException e) { + throw new DataValidationException(e.getMessage()); + + } + } + + private String validateNodeCredentials (JsonNode nodeCredentialsValue, String [] fields) { + Set msgSet = ConcurrentHashMap.newKeySet(); + String msg = ""; + for (String field : fields) { + if (field.contains(":")) { + String [] keys = field.split(":"); + if (!nodeCredentialsValue.hasNonNull(keys[0])) { + msgSet.add(keys[1]); + } + else { + if (!nodeCredentialsValue.get(keys[0]).hasNonNull(keys[1])) msgSet.add(keys[1]); + } + } + else { + if (!nodeCredentialsValue.hasNonNull(field)) msgSet.add(field); + } + } + if (msgSet.size() > 0) msg = "Device credentials are missing fields or mandatory value in this fields: " + String.join(", ", msgSet); + return msg; + } + + private void checkClientKey (JsonNode node) throws DataValidationException, DecoderException { + String modeName = node.get("securityConfigClientMode").asText(); + // checks security config + // HexDec Len = 32,64,128 + if (SecurityMode.PSK.name().equals(modeName)) { + String key = node.get("key").textValue(); + assertIf(key == null || key.isEmpty(), + "pre-shared-key mode, Client`s private key or id must be not null or empty."); + assertIf(!key.matches("-?[0-9a-fA-F]+"), + "pre-shared-key mode, Client`s private key or id must be HexDecimal format."); + assertIf(key.length()%32 != 0 , + "pre-shared-key mode, Client`s private key or id must be a multiple of 32."); + assertIf(key.length() > 128, + "pre-shared-key mode, Client`s private key or id must be not more than 128."); + String identity = node.get("identity").textValue(); + assertIf(identity == null || identity.isEmpty(), + "pre-shared-key mode, Client`s identity key must be not null or empty."); + } else if (SecurityMode.RPK.name().equals(modeName)) { + String value = node.get("key").textValue(); + assertIf(decodeRfc7250PublicKey(org.eclipse.leshan.core.util.Hex.decodeHex(((String) value).toCharArray())) == null, + "raw-public-key mode, Client`s public key or id must be RFC7250 encoded public key"); + } else if (SecurityMode.X509.name().equals(modeName)) { + String value = node.get("cert").textValue(); + if (value != null && !value.isEmpty()) { + assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, + "x509 mode, Client`s public key must be DER encoded X.509 certificate"); + } + } + } + + private void checkServerKey (JsonNode node, String serverType) throws DataValidationException { + String modeName = node.get("securityMode").asText(); + // checks security config + if (SecurityMode.RPK.name().equals(modeName)) { + checkRPKServer(node, serverType); + } else if (SecurityMode.X509.name().equals(modeName)) { + checkX509Server(node, serverType); + } + } + + protected void checkRPKServer(JsonNode node, String serverType) throws DataValidationException { + String value = node.get("clientSecretKey").textValue(); + assertIf(decodeRfc5958PrivateKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null, + "raw-public-key mode, " + serverType + " secret key must be RFC5958 encoded private key"); + value = node.get("clientPublicKeyOrId").textValue(); + assertIf(decodeRfc7250PublicKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null, + "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); + } + + protected void checkX509Server(JsonNode node, String serverType) throws DataValidationException { + String value = node.get("clientSecretKey").textValue(); + assertIf(decodeRfc5958PrivateKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null, + "x509 mode " + serverType + " secret key must be RFC5958 encoded private key"); + value = node.get("clientPublicKeyOrId").textValue(); + assertIf(decodeCertificate(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null, + "x509 mode " + serverType + " public key must be DER encoded X.509 certificate"); + + } + + protected void checkDeviceProfileServer (Map server, String serverType) throws DataValidationException { + // checks security config + String value = (String) server.get("serverPublicKey"); + if (SecurityMode.RPK.name().equals(server.get("securityMode"))) { + assertIf(decodeRfc7250PublicKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null, + "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); + } else if (SecurityMode.X509.name().equals(server.get("securityMode"))) { + assertIf(decodeCertificate(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null, + "x509 mode, " + serverType + " public key must be DER encoded X.509 certificate"); + } + } + + protected PrivateKey decodeRfc5958PrivateKey(byte[] encodedKey) { + try { + return SecurityUtil.privateKey.decode(encodedKey); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + protected PublicKey decodeRfc7250PublicKey(byte[] encodedKey) { + try { + return SecurityUtil.publicKey.decode(encodedKey); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + protected Certificate decodeCertificate(byte[] encodedCert) { + try { + return SecurityUtil.certificate.decode(encodedCert); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + protected static void assertIf(boolean condition, String message) throws DataValidationException { + if (condition) { + throw new DataValidationException(message); + } + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 720d50ad69..aacaccc73b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -54,6 +54,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; @@ -132,6 +133,9 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @Autowired private DashboardService dashboardService; + @Autowired + private DeviceCredentialsService deviceCredentialsService; + private final Lock findOrCreateLock = new ReentrantLock(); @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}") @@ -409,6 +413,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); } } + } else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) { + deviceCredentialsService.verifyLwm2mSecurityKeyDeviceProfile((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration); } List profileAlarms = deviceProfile.getProfileData().getAlarms(); diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh index dcf38b4f10..d623bfad42 100755 --- a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh +++ b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh @@ -321,7 +321,7 @@ cert_end_point() { } if [ "$client_start" -lt "$client_finish" ]; then - echo "Файл содержит, как минимум, одно слово Bash." + echo echo "==Start Client==" while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 64a95d0513..47d7e5d35b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -185,13 +185,13 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, * If the Object field “Mandatory” is “Mandatory” and the Object field “Instances” is “Single” then - * the number of Object Instance MUST be 1. * 1. == Multiple (true), == Optional (false) - - * Object Instance ID MIN_ID=0 MAX_ID=65535 (может ни одного не быть) + * Object Instance ID MIN_ID=0 MAX_ID=65535 (there may be none) * 2. == Multiple (true), == Mandatory (true) - - * Object Instance ID MIN_ID=0 MAX_ID=65535 (min один обязательный) + * Object Instance ID MIN_ID=0 MAX_ID=65535 (min one mandatory) * 3. == Single (false), == Optional (false) - - * Object Instance ID cnt_max = 1 cnt_min = 0 (может ни одного не быть) + * Object Instance ID cnt_max = 1 cnt_min = 0 (there may be none ) * 4. == Single (false), == Mandatory (true) - - * Object Instance ID cnt_max = cnt_min = 1 (всегда есть один) + * Object Instance ID cnt_max = cnt_min = 1 (there must always be one) */ addInstances = ($event: Event, control: AbstractControl): void => { From 6335f49c07c33ac0974927c67c09cf29f930fecf Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 8 Sep 2021 22:38:28 +0300 Subject: [PATCH 12/21] lwm2m: fix bug commit validate keys --- .../LwM2MServerSecurityInfoRepository.java | 196 ------------------ .../service/lwm2m/LwM2MServiceImpl.java | 71 +++++++ 2 files changed, 71 insertions(+), 196 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java create mode 100644 application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java deleted file mode 100644 index 8750fa466c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.lwm2m; - - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.SecurityMode; -import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.core.util.SecurityUtil; -import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.util.Map; - -@Slf4j -@Service -@RequiredArgsConstructor -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") -public class LwM2MServerSecurityInfoRepository { - - private final LwM2MTransportServerConfig serverConfig; - private final LwM2MTransportBootstrapConfig bootstrapConfig; - - public ServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer) { - ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig); - result.setBootstrapServerIs(bootstrapServer); - return result; - } - - private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) { - ServerSecurityConfig bsServ = new ServerSecurityConfig(); - bsServ.setServerId(serverConfig.getId()); - bsServ.setHost(serverConfig.getHost()); - bsServ.setPort(serverConfig.getPort()); - bsServ.setSecurityHost(serverConfig.getSecureHost()); - bsServ.setSecurityPort(serverConfig.getSecurePort()); - bsServ.setServerPublicKey(getPublicKey(serverConfig)); - return bsServ; - } - - private String getPublicKey(LwM2MSecureServerConfig config) { - try { - KeyStore keyStore = serverConfig.getKeyStoreValue(); - if (keyStore != null) { - X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(config.getCertificateAlias()); - return Hex.encodeHexString(serverCertificate.getPublicKey().getEncoded()); - } - } catch (Exception e) { - log.trace("Failed to fetch public key from key store!", e); - - } - return ""; - } - - public void verifySecurityKeyDevice(DeviceCredentials deviceCredentials) throws InvalidConfigurationException, JsonProcessingException { - JsonNode nodeCredentialsValue = deviceCredentials.getNodeCredentialsValue(); - checkClientKey (nodeCredentialsValue.get("client")); - checkServerKey (nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); - checkServerKey (nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); - } - - private void checkClientKey (JsonNode node) throws InvalidConfigurationException { - String modeName = node.get("securityConfigClientMode").asText(); - // checks security config - - if (SecurityMode.RPK.name().equals(modeName)) { - String value = node.get("key").textValue(); - assertIf(decodeRfc7250PublicKey(Hex.decodeHex(((String) value).toCharArray())) == null, - "raw-public-key mode, Client`s public key or id must be RFC7250 encoded public key"); - } else if (SecurityMode.X509.name().equals(modeName)) { - String value = node.get("cert").textValue(); - if (value != null && !value.isEmpty()) { - assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null, - "x509 mode, Client`s public key must be DER encoded X.509 certificate"); - } - } - - } - - private void checkServerKey (JsonNode node, String serverType) throws InvalidConfigurationException { - String modeName = node.get("securityMode").asText(); - // checks security config - if (SecurityMode.RPK.name().equals(modeName)) { - checkRPKServer(node, serverType); - } else if (SecurityMode.X509.name().equals(modeName)) { - checkX509Server(node, serverType); - } - } - - protected void checkRPKServer(JsonNode node, String serverType) throws InvalidConfigurationException { - String value = node.get("clientSecretKey").textValue(); - assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(value.toCharArray())) == null, - "raw-public-key mode, " + serverType + " secret key must be RFC5958 encoded private key"); - value = node.get("clientPublicKeyOrId").textValue(); - assertIf(decodeRfc7250PublicKey(Hex.decodeHex(value.toCharArray())) == null, - "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); - } - - protected void checkX509Server(JsonNode node, String serverType) throws InvalidConfigurationException { - String value = node.get("clientSecretKey").textValue(); - assertIf(decodeRfc5958PrivateKey(Hex.decodeHex(value.toCharArray())) == null, - "x509 mode " + serverType + " secret key must be RFC5958 encoded private key"); - value = node.get("clientPublicKeyOrId").textValue(); - assertIf(decodeCertificate(Hex.decodeHex(value.toCharArray())) == null, - "x509 mode " + serverType + " public key must be DER encoded X.509 certificate"); - - } - - public void verifySecurityKeyDeviceProfile(DeviceProfile deviceProfile) throws InvalidConfigurationException, JsonProcessingException { - Map serverBs = ((Lwm2mDeviceProfileTransportConfiguration)deviceProfile.getProfileData().getTransportConfiguration()).getBootstrap().getBootstrapServer(); - checkDeviceProfileServer (serverBs, "Servers: BootstrapServer`s"); - Map serverLwm2m = ((Lwm2mDeviceProfileTransportConfiguration)deviceProfile.getProfileData().getTransportConfiguration()).getBootstrap().getLwm2mServer(); - checkDeviceProfileServer (serverLwm2m, "Servers: Lwm2mServer`s"); - - } - - protected void checkDeviceProfileServer (Map server, String serverType) throws InvalidConfigurationException{ - // checks security config - String value = (String) server.get("serverPublicKey"); - if (SecurityMode.RPK.name().equals(server.get("securityMode"))) { - assertIf(decodeRfc7250PublicKey(Hex.decodeHex(value.toCharArray())) == null, - "raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key"); - } else if (SecurityMode.X509.name().equals(server.get("securityMode"))) { - assertIf(decodeCertificate(Hex.decodeHex(value.toCharArray())) == null, - "x509 mode, " + serverType + " public key must be DER encoded X.509 certificate"); - } - } - - protected PrivateKey decodeRfc5958PrivateKey(byte[] encodedKey) throws InvalidConfigurationException { - try { - return SecurityUtil.privateKey.decode(encodedKey); - } catch (IOException | GeneralSecurityException e) { - return null; - } - } - - protected PublicKey decodeRfc7250PublicKey(byte[] encodedKey) throws InvalidConfigurationException { - try { - return SecurityUtil.publicKey.decode(encodedKey); - } catch (IOException | GeneralSecurityException e) { - return null; - } - } - - protected Certificate decodeCertificate(byte[] encodedCert) throws InvalidConfigurationException { - try { - return SecurityUtil.certificate.decode(encodedCert); - } catch (IOException | GeneralSecurityException e) { - return null; - } - } - - protected static void assertIf(boolean condition, String message) throws InvalidConfigurationException { - if (condition) { - throw new InvalidConfigurationException(message); - } - } - - protected static boolean isEmpty(byte[] array) { - return array == null || array.length == 0; - } - - -} - diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java new file mode 100644 index 0000000000..3a85a94f2d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.lwm2m; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.util.Hex; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; +import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import java.security.KeyStore; +import java.security.cert.X509Certificate; + +@Slf4j +@Service +@RequiredArgsConstructor +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") +public class LwM2MServiceImpl implements LwM2MService { + + private final LwM2MTransportServerConfig serverConfig; + private final LwM2MTransportBootstrapConfig bootstrapConfig; + + @Override + public ServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer) { + ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig); + result.setBootstrapServerIs(bootstrapServer); + return result; + } + + private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) { + ServerSecurityConfig bsServ = new ServerSecurityConfig(); + bsServ.setServerId(serverConfig.getId()); + bsServ.setHost(serverConfig.getHost()); + bsServ.setPort(serverConfig.getPort()); + bsServ.setSecurityHost(serverConfig.getSecureHost()); + bsServ.setSecurityPort(serverConfig.getSecurePort()); + bsServ.setServerPublicKey(getPublicKey(serverConfig)); + return bsServ; + } + + private String getPublicKey(LwM2MSecureServerConfig config) { + try { + KeyStore keyStore = serverConfig.getKeyStoreValue(); + if (keyStore != null) { + X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(config.getCertificateAlias()); + return Hex.encodeHexString(serverCertificate.getPublicKey().getEncoded()); + } + } catch (Exception e) { + log.trace("Failed to fetch public key from key store!", e); + + } + return ""; + } +} + From 8dbacbd65f4cf182c2766e48cc154724907f3a3d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 9 Sep 2021 07:46:35 +0300 Subject: [PATCH 13/21] lwm2m: add to JacksonUtil new method "validateFieldsToTree" --- .../thingsboard/common/util/JacksonUtil.java | 23 +++++++++++++++++ .../device/DeviceCredentialsServiceImpl.java | 25 ++----------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java index 18b7abb67e..5ccfbd1461 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java @@ -23,6 +23,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Created by Valerii Sosliuk on 5/12/2017. @@ -118,4 +120,25 @@ public class JacksonUtil { public static JsonNode valueToTree(T value) { return OBJECT_MAPPER.valueToTree(value); } + + public static String validateFieldsToTree(JsonNode nodeCredentialsValue, String[] fields, String delimiter) { + try { + Set msgSet = ConcurrentHashMap.newKeySet(); + for (String field : fields) { + String[] keys = field.split(delimiter); + JsonNode nodeVal = nodeCredentialsValue; + for (String key : keys) { + if (!nodeVal.hasNonNull(key)) { + msgSet.add(keys[keys.length - 1]); + break; + } else { + nodeVal = nodeVal.get(key); + } + } + } + return String.join(", ", msgSet); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 9d0ff83491..1974f970cc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -260,14 +260,14 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen // LwM2MCredentialsValid credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), LwM2MCredentialsValid.class); JsonNode nodeCredentialsValue = JacksonUtil.toJsonNode(deviceCredentials.getCredentialsValue()); String [] fields = {"client", "bootstrap", "bootstrap:bootstrapServer", "bootstrap:lwm2mServer"}; - String validateMsg = validateNodeCredentials (nodeCredentialsValue, fields); + String validateMsg = JacksonUtil.validateFieldsToTree(nodeCredentialsValue, fields, ":"); if (validateMsg.isEmpty()) { checkClientKey(nodeCredentialsValue.get("client")); checkServerKey(nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer"); checkServerKey(nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer"); } else { - throw new DataValidationException(validateMsg); + throw new DataValidationException("Device credentials are missing fields or mandatory value in this fields: " + validateMsg); } } catch (DataValidationException | DecoderException e) { throw new DataValidationException(e.getMessage()); @@ -286,27 +286,6 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen } } - private String validateNodeCredentials (JsonNode nodeCredentialsValue, String [] fields) { - Set msgSet = ConcurrentHashMap.newKeySet(); - String msg = ""; - for (String field : fields) { - if (field.contains(":")) { - String [] keys = field.split(":"); - if (!nodeCredentialsValue.hasNonNull(keys[0])) { - msgSet.add(keys[1]); - } - else { - if (!nodeCredentialsValue.get(keys[0]).hasNonNull(keys[1])) msgSet.add(keys[1]); - } - } - else { - if (!nodeCredentialsValue.hasNonNull(field)) msgSet.add(field); - } - } - if (msgSet.size() > 0) msg = "Device credentials are missing fields or mandatory value in this fields: " + String.join(", ", msgSet); - return msg; - } - private void checkClientKey (JsonNode node) throws DataValidationException, DecoderException { String modeName = node.get("securityConfigClientMode").asText(); // checks security config From 9e9a8238db7c37bd2f3f7caac738202b80c92596 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 13 Sep 2021 19:01:04 +0300 Subject: [PATCH 14/21] Merge branch develop/3.3.2 into current --- .../AbstractLwM2MIntegrationTest.java | 2 +- .../dao/device/DeviceCredentialsService.java | 1 - .../dao/device/DeviceProfileServiceImpl.java | 2 +- transport/lwm2m/src/main/data/lwm2mserver.jks | Bin 3953 -> 3849 bytes 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java index eda896fa35..28d98693f1 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java @@ -177,7 +177,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest protected static final String ENDPOINT = "deviceAEndpoint"; - protected CoapClient client; + protected LwM2MTestClient client; private final LwM2MBootstrapCredentials defaultBootstrapCredentials; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java index 599d08df3b..a57d39776f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java @@ -35,5 +35,4 @@ public interface DeviceCredentialsService { void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); - void verifyLwm2mSecurityKeyDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) ; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index aacaccc73b..adbaff5389 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -414,7 +414,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } } } else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) { - deviceCredentialsService.verifyLwm2mSecurityKeyDeviceProfile((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration); +// deviceCredentialsService.verifyLwm2mSecurityKeyDeviceProfile((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration); } List profileAlarms = deviceProfile.getProfileData().getAlarms(); diff --git a/transport/lwm2m/src/main/data/lwm2mserver.jks b/transport/lwm2m/src/main/data/lwm2mserver.jks index 9f6748f8fdab7179b895eccd71421fe1565d27db..5fab824aa1b19928fe711701cd27857472a9e739 100644 GIT binary patch delta 3653 zcmV-L4!ZI29*G_zFoF*S0s#Xsf)2h02`Yw2hW8Bt2LYgh4zC1)4y!PN4yTbKM1Ly* zom9BjJH$VcW(h0D2xIlY6r2JB0K-rOftaAyg&4(nOgQGgqZXKQNpgf$5vgZ}((Ysq z4>NDXm)2g@Mz`$C_E~pf2sbe$VNxNWg7CwD5uTlTd647vCxxS`5^~+i%3oo$<=Ut7 z96^zpSju@r$84gNVErKYNtV0q%r!bu0bINIX>{=o{Yo;qIe}jww@|a@ui(dgepJ8kd7lOaQD49BSZNR~ob$&O zk7x0cF9b%YM6(;R& z>c*G%1)}{X$%f_0t8V_&LNQU|0xkI3^#2v9Vh5%nhr zdsYmeWIv&B_#`cG{+tpJXY%zh<=zWf`~HA8b=FeEgQU!~(4G ztKp3!X7^z8TDs_-``@hRMC~tFi0__dxX*0}y=Ku+K_v3MQFss)6Mo3)N80o-GGc*W zqtL?nMDyA^8LxWo6s#(8BiGE*b(LJ80%S2I&;7tsS(Iud<2!Pq_7 z>J=D1|4`9nwAA=Bq*ee8T-oXYHqazGaBBxrAXf*xG306XETw{w1^@SbxoZrvB@7;< zIy#$$kn-oXcBOFb)gkUIjST)S6{Ak92W>iRfVl+>usEb%U`2m36mhpc(00T)sv9GL zZ$jfE=kcjZc;`J2HCB6CQ+Eo7vSpHPUfWuethbwDt_0oBbK8itz))(CMpr(R5B)?P zr~DX{n}1`D_WP?4W03usm03LnEe=atcZtA`B*4ji@MFI(hQ39`@cD~Jm?|Q>hp}MW zBP@1lTH|X@8(n|F3|tdOGU;dyMo*r%EEoj4(AO?W6L_WzK@B8a=dAk*lyd}Y<7RD8 zDR_+ERe&|!dTY>%hp9MP7c&=^FE3G%ga}k|^ApCxZyug=Q~wXW5j~F7WcH?x(18$d$?BpD-!$GC$eMt*93*}cRz@aC95vE zbo>Sjw~sSB?w+EcFhyjs5{!UFV^-YTX%vVAhx>n^P-^UJXp-RJAa8XB*qyRCj4cb- zRtI9BFx%wgz?Us z>oeetM}}cMV%1rd6J2x=wbRcLN|8R~st2r>ihqh38kECo?4fkJ)xNFJY}~=Z($G^E zA4-3tRMmld%nq7rkiC7%j-v#>2eMo|rUrNcT5$ea>7}Xj%0nMK0{lqoAh{EZWMFU2 z?!>~Mgb!SzeS4;xt}k}YQPqtxR@Yn-@Hk5Wx~hyDCk?gu)|OLkjL1s{>wRy@#2VmJ znoX1$=4b9mXv3K@I|f|Vd$T-lnF@i5Gpv7p{faHOI1{2$$;VcAM3>05@M7iAS`rs$ z*>pBhPp@>$(p>6^lmSApfZ%ati8_sF%z5n?;Gd^})wsAe;;qNqGJ5EEVj_h^rjoiWJAij^KT^KiemaQ*LCt~Vc3%5HuZn3TM|`;hAj3=a2h^{>eVrKIz$R}Fvl zJmCbxlRb|8U1EGj>#%#?-0qpbg5%1jjtN{&7vZS1JWUwzOU*9CNd2I*Bh-IfF^e%EmB7aaH-T0k*XL=`2^+C)>&jSZDe07V z|I4%HQH%Z1cDBB=vQ{6O*i<~L$P2-tsvZF+z6@(L<9ULG}>YN;%*B} zo&7)}<#)+(z{3{ma15xSj5$<%gE8F$A`EY_1d*@2ih?hm5)@&g{7Mb)ksyDSG8&tf zy48HrJ=34^yU*BnZ-6)`d`kR{RlA-@FbKSdbT#z*a`oMRyDQ}(Y!Qz!7PXIi?fNU+8JZJq?OJ9`gFo!Nn# zJCbbf)Pv82Lg$-wQ^T0FuBCqjsZnQZ_=7L}nJGtg^kAfH_h>wCtmB=X(v5|JM&@4J z*!#O;?Iq-XrN~m6R?p#9m)=}89sSH4jHKB;+L4A}Oi}=aCpZbb)W4d$ycoK`a@(&nXPD_7-8se;v2?#i@ z5|D*$W`hvY#F2L^dgS||;U-%m<8vK$+$eenj(YkZGz`+!`utjH=I*+sSFly$5KZK- zFSX4GPUe4~V!<7M_@B_sv@R4UoRo~)Ch$#3{30wRJ}2$##*6+N0F0Nv8NK-)mpX% z@4ostl_4ISiSl`rqt%-|_XE@aqA8<&YKiWy4$Z;oCpZ5nke+|c+~anTHd3P{mchxm ziE75=L4LzN-xtubZ8rc8A5+5}2#^ait_(WiB%^Z9ZWf=I=!O~HFS36eL^p6)v7Y5I zHz=sWEy^;s*M!^Fc)G|#SVGq}IHPJQOMBumSC^kVQ=~>gBr+a+Rq^?u{AGuFAQC@k zga)8!>>xqoC)nH6A<3~Y3s`@$#AV!Yr@k@`^o)oX>E1sT~Ag1wYDw5<6 zKV?q$Z-+AvG4p83!1rXSjAb!yr3$w4j0RBTz~66KXsds>x#XDMYi3=pj>usTFx|%e z2eODYW|KtQ`#XAByPJWfo?Pl>Y`FFf85H$>vVpi1+tlTD%@T}rSh`0Qj&hHNA4Y?| zOPd^d=-Zj_qh z)daLS3Q_pFP7FV!9CMWn*||txn|zMf8}{Jbz&sV9Q4sV%qUMzf@94TQ9jiR|;Qyr6 zb5g1e=SHGl^<8Y;LJ>?l!$^!hP=4 zWm$j!_x{)#<)ro~78OK;d#S{RBNWNJ;i?W`h9Q=9m(#`#uhmanl9}D7KVW!QHs`I- zt_jMk?j3^zaB9wh8rlvdeMQfNAe?3OXp#e_MLJ>nJ;)IVeyYZ8MSt`=*@GH-9sr@< zs-P+(o5bz0Fg`FLFbM_)D-Ht!8U+9Z6xYT>#LaQo(*VtnzN_qMP4M!Fhy)b<1nu-# X_d}DEK0Izji}dfgHE9+C0|ADhaZ(kz delta 3758 zcmV;f4pH%m9`PO`FoF+l0s#Xsf)6GJ2`Yw2hW8Bt2LYgh4;KW24-+tg4-b(dM1S-i z{srVqTGKEDaK2q&C*civrQ!ku0K-rOftY_W?dJLp*IR#Gc*!!xkpxE?wSF%W|6}_C zVqQ(g1?5uTlGag0&^c3`lU=^W>izj-T7{iDN&27>DOY+qHvUmtH2n3den$f;82fv2 zmBu!DS_FEdwT7F%f~R0@YJ42Rk2T5+bK`W-6?=o4(zV>6+8)fW5y+KjISE|QdhbFS ze3x#V^%TSREb9F`ENS$a>+MLW9--%x8UZ&CF*7nUHaIjmG&nLaf&n3sK_!1B5v#8o zUP=N1_%{{P>a~$lh=*DN0|3KN1c8{f3i>!()nXP2mOE4kkxSw!Hox+qA(3G2T$Sb* z*?Hl6f+<7&D3!y&QLoC~J2mf1y$-(Ls@OYsgBij&LNQU)RNo3>bAG$3Rz_n)NE)9t=eP2Ke*b)I2qPlt>Tk8!{cz5t1oikCNS1 zCcHPY|G_#|MCQm~`l1{{Zx@20KQjKfbxrTjr&KpZc4U$#j1pA5yKoOjLyA zAGBtRSisO-F9=!rwy#ko&xnl-^7-qYb87KZ08QTTwwa1vZ=ran1-van5_znwqoC6M zr_C=XqZT_9YnId8=>2QPgIhw0Z_PpLqxzzwDJKeZ&V1+De>Yx@AC_nJo}lUFEuBgd z3kRt=5Aj2$cm<%_CIq0Z> z@~)y&cPOg!<~6^O5N;Xm8w2PQxLX=oC1T9Fh`EyU<1O`?ar}Ql1j@^4g1bno6P@tj zC07JVoS2;6Z2b+$a2G7Eevy=zj$U zJlkqdCriZmaQG9|!W(0b%weA%o$s+tLF#L|y&8udAAb!zZw<#J!r_#_pEN-J0dy!r z++6QEOW$*1fsB7N3YQpmS#Syy7N}6qV!5O40tCr!t^JA)rDSOgOai|;p6zJwj_q( z06;bY`T19Pyl*YNYd33n3o34U!PwIQV0-^!M>FqQK<6@X^Iam(6aXxv2QP+L%qMZF zzpH)egz$gNNw%pMeekD5#0E_YOg!>6^`PucYmlo^$snKT zFJC%2E4(7)0f7i+VLc>_rQ6Ft(WUq}KxyLb+H8LttD*Se=H^xfV6VpEYq-u7aF83W zarF6tIoO5FKce|qn6w8a*wrN;)R11|pQ~l@@DsWwJ=Y7PVF$(mO-9cei-Gb{geXMt zKa*N3RZh=+!>UZKaLN(;#tHEf4#dC&@2&`W(Srf*bzv*_n^)hj&_b01>&;J`31l z(1}w7mP1uT7WxvERM16tE#*6U+yFF7Upa5L*B+B-NDZl_P~M}tDe&okQhg$V;ktiD zV^NUY^@4!~l31+g=jB#3*!F2SgwB5|)$T&b{h4w)Yavalvk~mbMmiIaRvx%vS!uuk zL1UvtC}lR+*`TkDHmt-Dfi$6@A$E&oV}!>S2b^jqfzmh`t=2gS`Ke%xZ}|1F_Jp%w zrIKuDOSf(tpg8Qa7p|jm?YX||zTxo~NTRCBSyD4t!t`)fWiD70COw=UPp*Hev!tQn z^44DgA}8B?Oqf$ECe>t;R=}%78o!yc$Jh_pc1fd~zx_W!mmG0?QunF9#es|?Zm^?Z zlLCct^SEnEMz_Xuyk0d2c~c3vEy9!~s>fZ-L5Kw2n9JS3_|>QiHg_pbj@sDp$7p`g z2w0I=0~MDJ9|KJhU;|-5w+Dax9-52ik*iW);Nr|+y@AylI_9;$tzeFS_}=|rg%9h} zogN`kY)D%rURD$Uf8oZg->{#4{k;MJM~X;)f`$gUw`V0t)>5nb--SpGUjMbq4Wbzm zy{oJx6~I{-OW@_ne8&IlLm**XS-V|rFnBBGSEeI2T;nCdV=@7ymfnATIB4XLeZD`` z(Tu~>sovCMYKTS>6gqe1!M?i-G6s-$2MuZLbh9NlzvSRW7jf6C;x2wy%}N*eC@AHB zPUU=Gj9#JOXR4dmpGsE?48meK^)Lgn^-yrAiEDaD*LuS+2~^#oQ)rLOIJ^&56W)FZ=A}u4m8##UC&C-G*nMTgm(38T&SR z?1(FWOY!Ypa2%}h=t5t)INw@L;qJ!68jqPhx)L z-E1PfXH?2kRYVinz&+Rp#VL z^1Lmcc2$0}=)9z)c3^`+tN1GaG{r#ZL&iNXZ{3Q%cJt0lKf1b|aCAd|tlqyTN+FV~8O%+Y4J0IDXKP)S znZRdG8euiXnVsX*2a~-g9QZYD=CkiH5?J>2ldTr0U_z5I=?EsbWlGkM$Ab-!Hjf#R zDJ-FbsuvSjx@0s#MgBmzZ_J*~JVz6`$OvLmQayj>)fIbeN+CgEMQ${Z5f)cOK@l{AyD=YZZsu>XwMF}_E|Lr$}-Vlv$aBN8+U zIXHjxcXZ+c#=okogeWoalI4N6r^i+CP&}`d2|5mmgB2AjiANzX1dEsS!CYj!p>C0< z20euq-Mob|^0$+Mgtk09EG)Y>qW+FtJ0(>!uNe>#E(e|PNO8qAxigumEjsQUy#4c^ z7_6iUE{bIsE3H}m>WUXAD^-LAOJ2pyGj4w_lfaz|QqF%Dx+T$)CTmyo(#6N3(RY1{ z0Wt(;QX*T8c>P!Npaas2QHN48Y$?A0G;kW$7?cW;jH3^rX6y~4nf}|L^lQFk_%(F9 zLhK}#H+IPKl5cmQGRu|HSt5(!+*Q)?4P^chb+LpjDpQb|%dkUO!p|<~&-!`bvUq=_ zVXoO)iogr>B&(<$=h1?Abc@rfUD}-3T#DH2jOu zXAl25fHe(E3pcvz=t$e|htX7_%QAn^Qtl&Ln>|s^4)5A@t*?G9w!0Jf5!&V=Q-}UN z Date: Tue, 14 Sep 2021 07:36:36 +0300 Subject: [PATCH 15/21] Lwm2m test fix bug device credential --- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 13 +++++- .../sql/X509_NoTrustLwM2MIntegrationTest.java | 44 +++++++++++++++++++ ...va => X509_TrustLwM2MIntegrationTest.java} | 16 +------ 3 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java rename application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/{X509LwM2MIntegrationTest.java => X509_TrustLwM2MIntegrationTest.java} (69%) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 13bafd39d3..0e7c81b0be 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -30,8 +30,11 @@ import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; +import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecServerCredentials; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; @@ -267,9 +270,15 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); - LwM2MCredentials credentials = new LwM2MCredentials(); + + LwM2MDeviceCredentials credentials = new LwM2MDeviceCredentials(); credentials.setClient(clientCredentials); - credentials.setBootstrap(createBootstrapConfig()); + LwM2MBootstrapCredentials defaultBootstrapCredentials = new LwM2MBootstrapCredentials(); + NoSecServerCredentials serverCredentials = new NoSecServerCredentials(); + defaultBootstrapCredentials.setBootstrapServer(serverCredentials); + defaultBootstrapCredentials.setLwm2mServer(serverCredentials); + + credentials.setBootstrap(defaultBootstrapCredentials); deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); return device; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java new file mode 100644 index 0000000000..af721ab188 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.security.sql; + +import org.eclipse.leshan.client.object.Security; +import org.junit.Test; +import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; +import org.thingsboard.server.common.transport.util.SslUtil; +import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; + +import static org.eclipse.leshan.client.object.Security.x509; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_COAP_CONFIG; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_URI; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; + +public class X509_NoTrustLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { + + @Test + public void testConnectWithCertAndObserveTelemetry() throws Exception { + X509ClientCredentials credentials = new X509ClientCredentials(); + credentials.setEndpoint(ENDPOINT); + credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); + Security security = x509(SECURE_URI, + 123, + clientX509CertNotTrusted.getEncoded(), + clientPrivateKeyFromCert.getEncoded(), + serverX509Cert.getEncoded()); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java similarity index 69% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509LwM2MIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java index cd172bda46..9870a2a527 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java @@ -25,7 +25,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_COAP import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_URI; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { +public class X509_TrustLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @Test public void testConnectAndObserveTelemetry() throws Exception { @@ -39,18 +39,4 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); } -// @Ignore //See LwM2mClientContextImpl.unregister -// @Test -// public void testConnectWithCertAndObserveTelemetry() throws Exception { -// X509ClientCredentials credentials = new X509ClientCredentials(); -// credentials.setEndpoint(ENDPOINT); -// credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); -// Security security = x509(SECURE_URI, -// 123, -// clientX509CertNotTrusted.getEncoded(), -// clientPrivateKeyFromCert.getEncoded(), -// serverX509Cert.getEncoded()); -// super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); -// } - } From d808ba04bfbcd8ad0b9065f05e0ea9f8055fc687 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Sep 2021 19:50:22 +0300 Subject: [PATCH 16/21] Lwm2m test OTA fix bug. Delete duplicate code. --- .../AbstractLwM2MIntegrationTest.java | 309 +++++------------- .../lwm2m/client/LwM2MTestClient.java | 2 +- .../ota/AbstractOtaLwM2MIntegrationTest.java | 76 +++++ .../sql/OtaLwM2MIntegrationTest.java | 106 +++--- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 167 +--------- .../AbstractSecurityLwM2MIntegrationTest.java | 176 ++++++++++ .../sql/NoSecLwM2MIntegrationTest.java | 7 +- .../security/sql/PskLwm2mIntegrationTest.java | 6 +- .../security/sql/RpkLwM2MIntegrationTest.java | 4 +- .../sql/X509_NoTrustLwM2MIntegrationTest.java | 6 +- .../sql/X509_TrustLwM2MIntegrationTest.java | 4 +- .../DefaultLwM2MAttributesService.java | 7 +- .../ota/DefaultLwM2MOtaUpdateService.java | 6 + 13 files changed, 419 insertions(+), 457 deletions(-) rename application/src/test/java/org/thingsboard/server/transport/lwm2m/{security => }/AbstractLwM2MIntegrationTest.java (51%) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java rename application/src/test/java/org/thingsboard/server/transport/lwm2m/{security => ota}/sql/OtaLwM2MIntegrationTest.java (74%) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java similarity index 51% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 28d98693f1..225ae056e9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -13,20 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.security; +package org.thingsboard.server.transport.lwm2m; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.io.IOUtils; -import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; -import org.eclipse.leshan.core.util.Hex; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.util.SocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -35,12 +30,12 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; +import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecServerCredentials; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; @@ -62,41 +57,18 @@ import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; - -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.security.AlgorithmParameters; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.KeySpec; import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import static org.eclipse.leshan.client.object.Security.noSec; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; -import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; @DaoSqlTest public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { - protected final String TRANSPORT_CONFIGURATION = "{\n" + + protected String transportConfiguration = "{\n" + " \"type\": \"LWM2M\",\n" + " \"observeAttr\": {\n" + " \"keyName\": {\n" + @@ -148,135 +120,23 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest protected DeviceProfile deviceProfile; protected ScheduledExecutorService executor; protected TbTestWebSocketClient wsClient; - - protected final PublicKey clientPublicKey; // client public key used for RPK - protected final PrivateKey clientPrivateKey; // client private key used for RPK - protected final PublicKey serverPublicKey; // server public key used for RPK - protected final PrivateKey serverPrivateKey; // server private key used for RPK - - // client private key used for X509 - protected final PrivateKey clientPrivateKeyFromCert; - // server private key used for X509 - protected final PrivateKey serverPrivateKeyFromCert; - // client certificate signed by rootCA with a good CN (CN start by leshan_integration_test) - protected final X509Certificate clientX509Cert; - // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test) - protected final X509Certificate clientX509CertWithBadCN; - // client certificate self-signed with a good CN (CN start by leshan_integration_test) - protected final X509Certificate clientX509CertSelfSigned; - // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test) - protected final X509Certificate clientX509CertNotTrusted; - // server certificate signed by rootCA - protected final X509Certificate serverX509Cert; - // self-signed server certificate - protected final X509Certificate serverX509CertSelfSigned; - // rootCA used by the server - protected final X509Certificate rootCAX509Cert; - // certificates trustedby the server (should contain rootCA) - protected final Certificate[] trustedCertificates = new Certificate[1]; - - protected static final String ENDPOINT = "deviceAEndpoint"; - protected LwM2MTestClient client; - private final LwM2MBootstrapCredentials defaultBootstrapCredentials; + private String[] resources; + private String endpoint; public AbstractLwM2MIntegrationTest() { -// create client credentials - try { - // Get point values - byte[] publicX = Hex - .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); - byte[] publicY = Hex - .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); - byte[] privateS = Hex - .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); - - // Get Elliptic Curve Parameter spec for secp256r1 - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - - // Create key specs - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - - // Get keys - clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - - // Get certificates from key store - char[] clientKeyStorePwd = "client".toCharArray(); - KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) { - clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); - } - - clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd); - clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client"); - clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn"); - clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed"); - clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted"); - } catch (GeneralSecurityException | IOException e) { - throw new RuntimeException(e); - } - - // create server credentials - try { - // Get point values - byte[] publicX = Hex - .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); - byte[] publicY = Hex - .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); - byte[] privateS = Hex - .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); - - // Get Elliptic Curve Parameter spec for secp256r1 - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - - // Create key specs - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - - // Get keys - serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - - // Get certificates from key store - char[] serverKeyStorePwd = "server".toCharArray(); - KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) { - serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd); - } - - serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); - rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA"); - serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server"); - serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed"); - trustedCertificates[0] = rootCAX509Cert; - } catch (GeneralSecurityException | IOException e) { - throw new RuntimeException(e); - } - - defaultBootstrapCredentials = new LwM2MBootstrapCredentials(); - + this.defaultBootstrapCredentials = new LwM2MBootstrapCredentials(); NoSecServerCredentials serverCredentials = new NoSecServerCredentials(); - - defaultBootstrapCredentials.setBootstrapServer(serverCredentials); - defaultBootstrapCredentials.setLwm2mServer(serverCredentials); + this.defaultBootstrapCredentials.setBootstrapServer(serverCredentials); + this.defaultBootstrapCredentials.setLwm2mServer(serverCredentials); } - @Before - public void beforeTest() throws Exception { + public void init () throws Exception{ executor = Executors.newScheduledThreadPool(10, ThingsBoardThreadFactory.forName("test-lwm2m-scheduled")); loginTenantAdmin(); - String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; - for (String resourceName : resources) { + for (String resourceName : this.resources) { TbResource lwModel = new TbResource(); lwModel.setResourceType(ResourceType.LWM2M_MODEL); lwModel.setTitle(resourceName); @@ -291,9 +151,57 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest wsClient = buildAndConnectWebSocketClient(); } + @Before + public void beforeTest() throws Exception { + this.init(); + } + + @After + public void after() { + executor.shutdownNow(); + wsClient.close(); + clientDestroy(); + } + + public void basicTestConnectionObserveTelemetry(Security security, + LwM2MClientCredentials credentials, + NetworkConfig coapConfig, + String endpoint) throws Exception { + createDeviceProfile(transportConfiguration); + Device device = createDevice(credentials); + + SingleEntityFilter sef = new SingleEntityFilter(); + sef.setSingleEntity(device.getId()); + LatestValueCmd latestCmd = new LatestValueCmd(); + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); + + wsClient.send(mapper.writeValueAsString(wrapper)); + wsClient.waitForReply(); + + wsClient.registerWaitForUpdate(); + this.endpoint = endpoint; + createNewClient (security, coapConfig); + String msg = wsClient.waitForUpdate(); + + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); + Assert.assertEquals(1, update.getCmdId()); + List eData = update.getUpdate(); + Assert.assertNotNull(eData); + Assert.assertEquals(1, eData.size()); + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); + Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); + } + protected void createDeviceProfile(String transportConfiguration) throws Exception { deviceProfile = new DeviceProfile(); - deviceProfile.setName("LwM2M"); deviceProfile.setType(DeviceProfileType.DEFAULT); deviceProfile.setTenantId(tenantId); @@ -325,106 +233,39 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); LwM2MDeviceCredentials credentials = new LwM2MDeviceCredentials(); - credentials.setClient(clientCredentials); credentials.setBootstrap(defaultBootstrapCredentials); deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); + Thread.sleep(1000); return device; } - - protected OtaPackageInfo createFirmware() throws Exception { - String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; - - OtaPackageInfo firmwareInfo = new OtaPackageInfo(); - firmwareInfo.setDeviceProfileId(deviceProfile.getId()); - firmwareInfo.setType(FIRMWARE); - firmwareInfo.setTitle("My firmware"); - firmwareInfo.setVersion("v1.0"); - - OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); - - MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1}); - - return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); + public NoSecClientCredentials createNoSecClientCredentials(String endpoint) { + NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); + clientCredentials.setEndpoint(endpoint); + return clientCredentials; } - protected OtaPackageInfo createSoftware() throws Exception { - String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; - - OtaPackageInfo swInfo = new OtaPackageInfo(); - swInfo.setDeviceProfileId(deviceProfile.getId()); - swInfo.setType(SOFTWARE); - swInfo.setTitle("My sw"); - swInfo.setVersion("v1.0"); - - OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", swInfo, OtaPackageInfo.class); - - MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1}); + public void setResources (String[] resources) { + this.resources = resources; + } - return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); + public void setEndpoint (String endpoint) { + this.endpoint = endpoint; } - protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { - MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); - postRequest.file(content); - setJwtToken(postRequest); - return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class); + public void createNewClient (Security security, NetworkConfig coapConfig) throws Exception { + clientDestroy(); + client = new LwM2MTestClient(this.executor, this.endpoint); + int clientPort = SocketUtils.findAvailableTcpPort(); + client.init(security, coapConfig, clientPort); } - @After - public void after() { - executor.shutdownNow(); - wsClient.close(); + private void clientDestroy() { if (client != null) { client.destroy(); } } - - public void basicTestConnectionObserveTelemetry(Security security, - LwM2MClientCredentials credentials, - NetworkConfig coapConfig, - String endpoint) throws Exception { - LwM2MTestClient client = null; - try { - createDeviceProfile(TRANSPORT_CONFIGURATION); - Device device = createDevice(credentials); - - SingleEntityFilter sef = new SingleEntityFilter(); - sef.setSingleEntity(device.getId()); - LatestValueCmd latestCmd = new LatestValueCmd(); - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - wsClient.send(mapper.writeValueAsString(wrapper)); - wsClient.waitForReply(); - - wsClient.registerWaitForUpdate(); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(security, coapConfig, clientPort); - String msg = wsClient.waitForUpdate(); - - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); - Assert.assertEquals(1, update.getCmdId()); - List eData = update.getUpdate(); - Assert.assertNotNull(eData); - Assert.assertEquals(1, eData.size()); - Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); - Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); - } finally { - if (client != null) { - client.destroy(); - } - } - } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 5c36f9820f..3fc83625ba 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -61,12 +61,12 @@ import static org.eclipse.leshan.core.LwM2mId.LOCATION; import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; @Slf4j diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java new file mode 100644 index 0000000000..e78d74bdcd --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.ota; + +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; + +@DaoSqlTest +public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { + + private final String[] resources = new String[]{"3.xml", "5.xml", "9.xml"}; + + public AbstractOtaLwM2MIntegrationTest() { + setResources(this.resources); + } + + protected OtaPackageInfo createFirmware() throws Exception { + String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; + + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); + firmwareInfo.setDeviceProfileId(deviceProfile.getId()); + firmwareInfo.setType(FIRMWARE); + firmwareInfo.setTitle("My firmware"); + firmwareInfo.setVersion("v1.0"); + + OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); + + MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1}); + + return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); + } + + protected OtaPackageInfo createSoftware() throws Exception { + String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; + + OtaPackageInfo swInfo = new OtaPackageInfo(); + swInfo.setDeviceProfileId(deviceProfile.getId()); + swInfo.setType(SOFTWARE); + swInfo.setTitle("My sw"); + swInfo.setVersion("v1.0"); + + OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", swInfo, OtaPackageInfo.class); + + MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1}); + + return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); + } + + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { + MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); + postRequest.file(content); + setJwtToken(postRequest); + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java similarity index 74% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java index 96006cbe67..de7c432564 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java @@ -13,21 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.security.sql; +package org.thingsboard.server.transport.lwm2m.ota.sql; import com.fasterxml.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; -import org.springframework.util.SocketUtils; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; -import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest; import java.util.Arrays; import java.util.Collections; @@ -41,9 +38,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.hasSize; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; -import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM; -import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM; -import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; @@ -56,7 +50,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; @Slf4j -public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { +public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { public static final int TIMEOUT = 30; private final String OTA_TRANSPORT_CONFIGURATION = "{\n" + @@ -139,56 +133,60 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @Test public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile() throws Exception { String endpoint = "WithoutFirmwareInfoDevice"; - createDeviceProfile(TRANSPORT_CONFIGURATION); - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(endpoint); - Device device = createDevice(clientCredentials); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(SECURITY, COAP_CONFIG, clientPort); + setEndpoint(endpoint); + createDeviceProfile(transportConfiguration); + NoSecClientCredentials credentials = createNoSecClientCredentials(endpoint); + final Device device = createDevice(credentials); + createNewClient(SECURITY, COAP_CONFIG); - Thread.sleep(1000); - - device.setFirmwareId(createFirmware().getId()); - device = doPost("/api/device", device, Device.class); + Thread.sleep(1000); - Thread.sleep(5000); + device.setFirmwareId(createFirmware().getId()); + final Device savedDevice = doPost("/api/device", device, Device.class); - List ts = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/values/timeseries?keys=fw_state", new TypeReference<>() { - })); + Thread.sleep(1000); - List statuses = ts.stream().map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); + assertThat(savedDevice).as("saved device").isNotNull(); + assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); - List expectedStatuses = Collections.singletonList(FAILED); + List ts = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + + savedDevice.getId().getId() + "/values/timeseries?keys=fw_state", new TypeReference<>() {})); + List statuses = ts.stream().map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); + List expectedStatuses = Collections.singletonList(FAILED); - Assert.assertEquals(expectedStatuses, statuses); + Assert.assertEquals(expectedStatuses, statuses); } @Test public void testFirmwareUpdateByObject5() throws Exception { String endpoint = "Ota5_Device"; - List expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, UPDATING, UPDATED); + setEndpoint(endpoint); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(endpoint); - Device device = createDevice(clientCredentials); + NoSecClientCredentials credentials = createNoSecClientCredentials(endpoint); + final Device device = createDevice(credentials); + createNewClient(SECURITY, COAP_CONFIG); + + Thread.sleep(1000); device.setFirmwareId(createFirmware().getId()); - Device saveDevice = doPost("/api/device", device, Device.class); + final Device savedDevice = doPost("/api/device", device, Device.class); + Thread.sleep(1000); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(SECURITY, COAP_CONFIG, clientPort); + assertThat(savedDevice).as("saved device").isNotNull(); + assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); + final List expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, UPDATING, UPDATED); List ts = await("await on timeseries") .atMost(30, TimeUnit.SECONDS) - .until(() -> toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + saveDevice.getId().getId() + "/values/timeseries?orderBy=ASC&keys=fw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() { + .until(() -> toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + + savedDevice.getId().getId() + "/values/timeseries?orderBy=ASC&keys=fw_state&startTs=0&endTs=" + + System.currentTimeMillis(), new TypeReference<>() { })), hasSize(expectedStatuses.size())); - - - List statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); - + List statuses = ts.stream().sorted(Comparator + .comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString) + .map(OtaPackageUpdateStatus::valueOf) + .collect(Collectors.toList()); Assert.assertEquals(expectedStatuses, statuses); } @@ -200,28 +198,25 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { * */ @Test public void testSoftwareUpdateByObject9() throws Exception { - //given - final List expectedStatuses = List.of( - QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED); String endpoint = "Ota9_Device"; + setEndpoint(endpoint); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(endpoint); - final Device device = createDevice(clientCredentials); - device.setSoftwareId(createSoftware().getId()); + NoSecClientCredentials credentials = createNoSecClientCredentials(endpoint); + final Device device = createDevice(credentials); + createNewClient(SECURITY, COAP_CONFIG); + + Thread.sleep(1000); + device.setSoftwareId(createSoftware().getId()); final Device savedDevice = doPost("/api/device", device, Device.class); //sync call + Thread.sleep(1000); assertThat(savedDevice).as("saved device").isNotNull(); assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); - //when - log.warn("Init the client..."); - client = new LwM2MTestClient(executor, endpoint); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(SECURITY, COAP_CONFIG, clientPort); - + final List expectedStatuses = List.of( + QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED); log.warn("AWAIT atMost {} SECONDS on timeseries List by API with list size {}...", TIMEOUT, expectedStatuses.size()); List ts = await("await on timeseries") .atMost(30, TimeUnit.SECONDS) @@ -230,9 +225,10 @@ public class OtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { ts.sort(Comparator.comparingLong(TsKvEntry::getTs)); log.warn("Ts ordered: {}", ts); - ts.forEach((x) -> log.warn("ts: {} ", x)); - - List statuses = ts.stream().map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList()); + ts.forEach((x) -> log.warn("ts: { Thread.sleep(1000);} ", x)); + List statuses = ts.stream().map(KvEntry::getValueAsString) + .map(OtaPackageUpdateStatus::valueOf) + .collect(Collectors.toList()); log.warn("Converted ts to statuses: {}", statuses); assertThat(statuses).isEqualTo(expectedStatuses); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 0e7c81b0be..83bb46e934 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -15,46 +15,14 @@ */ package org.thingsboard.server.transport.lwm2m.rpc; -import com.fasterxml.jackson.core.type.TypeReference; -import org.apache.commons.io.IOUtils; -import org.junit.After; -import org.junit.Assert; import org.junit.Before; -import org.springframework.util.SocketUtils; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.DeviceProfileProvisionType; -import org.thingsboard.server.common.data.DeviceProfileType; -import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapCredentials; -import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; -import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; -import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecServerCredentials; -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; -import org.thingsboard.server.common.data.device.profile.DeviceProfileData; -import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; -import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.common.data.security.DeviceCredentialsType; -import org.thingsboard.server.controller.AbstractWebsocketTest; import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapServers; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MServerBootstrap; -import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; - -import java.util.Base64; +import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Predicate; @@ -63,18 +31,9 @@ import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST_BS; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT_BS; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT_BS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID_BS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; @@ -89,7 +48,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; @DaoSqlTest -public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketTest { +public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { protected final String RPC_TRANSPORT_CONFIGURATION = "{\n" + " \"type\": \"LWM2M\",\n" + @@ -152,10 +111,6 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT protected static final String ENDPOINT_RPC = "deviceEndpointRpc"; protected ScheduledExecutorService executor; protected TbTestWebSocketClient wsClient; - protected DeviceProfile deviceProfile; - - protected NoSecClientCredentials clientCredentials; - protected LwM2MTestClient client; protected String deviceId; public Set expectedObjects; public Set expectedObjectIdVers; @@ -174,34 +129,25 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT protected String objectIdVer_50 = "/50"; protected String objectIdVer_3303; - public AbstractRpcLwM2MIntegrationTest(){ } + public AbstractRpcLwM2MIntegrationTest(){ + setResources(resources); + setEndpoint(ENDPOINT_RPC); + } @Before public void beforeTest() throws Exception { - executor = Executors.newScheduledThreadPool(10, ThingsBoardThreadFactory.forName("test-lwm2m-rpc-scheduled")); - loginTenantAdmin(); - wsClient = buildAndConnectWebSocketClient(); + init(); createDeviceProfile(RPC_TRANSPORT_CONFIGURATION); - clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(ENDPOINT_RPC); - Device device = createDevice(clientCredentials); + + Thread.sleep(1000); + + NoSecClientCredentials credentials = createNoSecClientCredentials(ENDPOINT_RPC); + final Device device = createDevice(credentials); deviceId = device.getId().getId().toString(); - client = new LwM2MTestClient(executor, ENDPOINT_RPC); - int clientPort = SocketUtils.findAvailableTcpPort(); - client.init(SECURITY, COAP_CONFIG, clientPort); - for (String resourceName : resources) { - TbResource lwModel = new TbResource(); - lwModel.setResourceType(ResourceType.LWM2M_MODEL); - lwModel.setTitle(resourceName); - lwModel.setFileName(resourceName); - lwModel.setTenantId(tenantId); - byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); - lwModel.setData(Base64.getEncoder().encodeToString(bytes)); - lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() { - }); - Assert.assertNotNull(lwModel); - } - wsClient = buildAndConnectWebSocketClient(); + createNewClient (SECURITY, COAP_CONFIG); + + Thread.sleep(1000); + expectedObjects = ConcurrentHashMap.newKeySet(); expectedObjectIdVers = ConcurrentHashMap.newKeySet(); expectedInstances = ConcurrentHashMap.newKeySet(); @@ -238,76 +184,6 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT objectInstanceIdVer_9 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + SOFTWARE_MANAGEMENT)).findFirst().get(); } - protected void createDeviceProfile(String transportConfiguration) throws Exception { - deviceProfile = new DeviceProfile(); - - deviceProfile.setName("LwM2M_RPC"); - deviceProfile.setType(DeviceProfileType.DEFAULT); - deviceProfile.setTenantId(tenantId); - deviceProfile.setTransportType(DeviceTransportType.LWM2M); - deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); - deviceProfile.setDescription(deviceProfile.getName()); - - DeviceProfileData deviceProfileData = new DeviceProfileData(); - deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); - deviceProfileData.setProvisionConfiguration(new DisabledDeviceProfileProvisionConfiguration(null)); - deviceProfileData.setTransportConfiguration(JacksonUtil.fromString(transportConfiguration, Lwm2mDeviceProfileTransportConfiguration.class)); - deviceProfile.setProfileData(deviceProfileData); - - deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); - Assert.assertNotNull(deviceProfile); - } - - - protected Device createDevice(LwM2MClientCredentials clientCredentials) throws Exception { - Device device = new Device(); - device.setName("Device A"); - device.setDeviceProfileId(deviceProfile.getId()); - device.setTenantId(tenantId); - device = doPost("/api/device", device, Device.class); - Assert.assertNotNull(device); - DeviceCredentials deviceCredentials = - doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); - Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); - deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); - - LwM2MDeviceCredentials credentials = new LwM2MDeviceCredentials(); - credentials.setClient(clientCredentials); - LwM2MBootstrapCredentials defaultBootstrapCredentials = new LwM2MBootstrapCredentials(); - NoSecServerCredentials serverCredentials = new NoSecServerCredentials(); - defaultBootstrapCredentials.setBootstrapServer(serverCredentials); - defaultBootstrapCredentials.setLwm2mServer(serverCredentials); - - credentials.setBootstrap(defaultBootstrapCredentials); - deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); - doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); - return device; - } - - protected LwM2MBootstrapConfig createBootstrapConfig() { - LwM2MBootstrapConfig bootstrap = new LwM2MBootstrapConfig(); - LwM2MBootstrapServers servers = new LwM2MBootstrapServers(); - servers.setShortId(SHORT_SERVER_ID); - bootstrap.setServers(servers); - LwM2MServerBootstrap server = new LwM2MServerBootstrap(); - server.setHost(HOST); - server.setPort(PORT); - server.setSecurityHost(HOST); - server.setSecurityPort(SECURE_PORT); - server.setServerId(servers.getShortId()); - server.setBootstrapServerIs(false); - bootstrap.setLwm2mServer(server); - LwM2MServerBootstrap serverBS = new LwM2MServerBootstrap(); - serverBS.setHost(HOST_BS); - serverBS.setPort(PORT_BS); - serverBS.setSecurityHost(HOST_BS); - serverBS.setSecurityPort(SECURE_PORT_BS); - serverBS.setServerId(SHORT_SERVER_ID_BS); - serverBS.setBootstrapServerIs(true); - bootstrap.setBootstrapServer(serverBS); - return bootstrap; - } - protected String pathIdVerToObjectId(String pathIdVer) { if (pathIdVer.contains("_")){ String [] objVer = pathIdVer.split("/"); @@ -316,15 +192,4 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractWebsocketT } return pathIdVer; } - - @After - public void after() { - if (client != null) { - client.destroy(); - } - executor.shutdownNow(); - if (wsClient != null) { - wsClient.close(); - } - } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java new file mode 100644 index 0000000000..491c39a9f9 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -0,0 +1,176 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.security; + +import org.eclipse.leshan.core.util.Hex; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapCredentials; +import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecServerCredentials; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.KeySpec; + +@DaoSqlTest +public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { + + protected final String pskIdentity; // client public key or id used for PSK + protected final String pskKey; // client private/secret key used for PSK + protected final PublicKey clientPublicKey; // client public key used for RPK + protected final PrivateKey clientPrivateKey; // client private key used for RPK + protected final PublicKey serverPublicKey; // server public key used for RPK + protected final PrivateKey serverPrivateKey; // server private key used for RPK + + // client private key used for X509 + protected final PrivateKey clientPrivateKeyFromCert; + // server private key used for X509 + protected final PrivateKey serverPrivateKeyFromCert; + // client certificate signed by rootCA with a good CN (CN start by leshan_integration_test) + protected final X509Certificate clientX509Cert; + // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test) + protected final X509Certificate clientX509CertWithBadCN; + // client certificate self-signed with a good CN (CN start by leshan_integration_test) + protected final X509Certificate clientX509CertSelfSigned; + // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test) + protected final X509Certificate clientX509CertNotTrusted; + // server certificate signed by rootCA + protected final X509Certificate serverX509Cert; + // self-signed server certificate + protected final X509Certificate serverX509CertSelfSigned; + // rootCA used by the server + protected final X509Certificate rootCAX509Cert; + // certificates trustedby the server (should contain rootCA) + protected final Certificate[] trustedCertificates = new Certificate[1]; + + protected static final String ENDPOINT = "deviceAEndpoint"; + + protected LwM2MTestClient client; + + private final LwM2MBootstrapCredentials defaultBootstrapCredentials; + + private final String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; + + public AbstractSecurityLwM2MIntegrationTest() { + // create client credentials + setResources(this.resources); + setEndpoint(ENDPOINT); + try { + // Get keys PSK + this.pskIdentity = "SOME_PSK_ID"; + this.pskKey = "73656372657450534b73656372657450"; + + // Get point values + byte[] publicX = Hex + .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); + byte[] publicY = Hex + .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); + byte[] privateS = Hex + .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); + + // Get Elliptic Curve Parameter spec for secp256r1 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + + // Create key specs + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); + + // Get keys RPK + clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); + + // Get certificates from key store + char[] clientKeyStorePwd = "client".toCharArray(); + KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) { + clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); + } + + clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd); + clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client"); + clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn"); + clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed"); + clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted"); + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } + + // create server credentials + try { + // Get point values + byte[] publicX = Hex + .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); + byte[] publicY = Hex + .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); + byte[] privateS = Hex + .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); + + // Get Elliptic Curve Parameter spec for secp256r1 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + + // Create key specs + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); + + // Get keys + serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); + + // Get certificates from key store + char[] serverKeyStorePwd = "server".toCharArray(); + KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) { + serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd); + } + + serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); + rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA"); + serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server"); + serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed"); + trustedCertificates[0] = rootCAX509Cert; + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } + + defaultBootstrapCredentials = new LwM2MBootstrapCredentials(); + + NoSecServerCredentials serverCredentials = new NoSecServerCredentials(); + + defaultBootstrapCredentials.setBootstrapServer(serverCredentials); + defaultBootstrapCredentials.setLwm2mServer(serverCredentials); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index af13dd62c4..1d278354b5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -18,18 +18,17 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; @Slf4j -public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { +public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { @Test public void testConnectAndObserveTelemetry() throws Exception { - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(ENDPOINT); + NoSecClientCredentials clientCredentials = createNoSecClientCredentials(ENDPOINT); super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index cd30ff52e8..23b60482f0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -19,7 +19,7 @@ import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import java.nio.charset.StandardCharsets; @@ -28,12 +28,10 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_COAP import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_URI; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -public class PskLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest { +public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { - String pskIdentity = "SOME_PSK_ID"; - String pskKey = "73656372657450534b73656372657450"; PSKClientCredentials clientCredentials = new PSKClientCredentials(); clientCredentials.setEndpoint(ENDPOINT); clientCredentials.setKey(pskKey); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index 02d57d6782..fdfa56957c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -19,14 +19,14 @@ import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import static org.eclipse.leshan.client.object.Security.rpk; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_COAP_CONFIG; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_URI; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -public class RpkLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { +public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { @Test public void testConnectWithRPKAndObserveTelemetry() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java index af721ab188..f6e08f0e3d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -19,14 +19,14 @@ import org.eclipse.leshan.client.object.Security; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; import org.thingsboard.server.common.transport.util.SslUtil; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import static org.eclipse.leshan.client.object.Security.x509; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_COAP_CONFIG; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_URI; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -public class X509_NoTrustLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { +public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { @Test public void testConnectWithCertAndObserveTelemetry() throws Exception { @@ -34,7 +34,7 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractLwM2MIntegrationTe credentials.setEndpoint(ENDPOINT); credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); Security security = x509(SECURE_URI, - 123, + SHORT_SERVER_ID, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java index 9870a2a527..28289b32bf 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java @@ -18,14 +18,14 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; -import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import static org.eclipse.leshan.client.object.Security.x509; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_COAP_CONFIG; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_URI; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; -public class X509_TrustLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { +public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { @Test public void testConnectAndObserveTelemetry() throws Exception { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index 38b4790216..2df47e3bfa 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -153,7 +153,12 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { newSoftwareTag = getStrValue(tsKvProto); } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) { newSoftwareUrl = getStrValue(tsKvProto); - } else { + } else if (!DefaultLwM2MOtaUpdateService.FIRMWARE_SIZE.equals(attrName) && + !DefaultLwM2MOtaUpdateService.FIRMWARE_CHECKSUM.equals(attrName) && + !DefaultLwM2MOtaUpdateService.FIRMWARE_CHECKSUM_ALGORITHM.equals(attrName) && + !DefaultLwM2MOtaUpdateService.SOFTWARE_SIZE.equals(attrName) && + !DefaultLwM2MOtaUpdateService.SOFTWARE_CHECKSUM.equals(attrName) && + !DefaultLwM2MOtaUpdateService.SOFTWARE_CHECKSUM_ALGORITHM.equals(attrName)){ otherAttributes.add(tsKvProto); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 8335057caa..f25f826c30 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -87,10 +87,16 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); public static final String FIRMWARE_TAG = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TAG); public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL); + public static final String FIRMWARE_SIZE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE); + public static final String FIRMWARE_CHECKSUM_ALGORITHM = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM); + public static final String FIRMWARE_CHECKSUM = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM); public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION); public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); public static final String SOFTWARE_TAG = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TAG); public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); + public static final String SOFTWARE_SIZE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE); + public static final String SOFTWARE_CHECKSUM_ALGORITHM = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM); + public static final String SOFTWARE_CHECKSUM = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM); public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "tbfw"; public static final String SOFTWARE_UPDATE_COAP_RESOURCE = "tbsw"; From 604b7e380f11b8c8cdebfa7cee87fe68493fdfd8 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Sep 2021 23:06:39 +0300 Subject: [PATCH 17/21] Lwm2m test OTA fix bug. Object9. --- .../DefaultLwM2MAttributesService.java | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index 2df47e3bfa..c1c995e687 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResourceInstance; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; @@ -137,28 +138,25 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { List otherAttributes = new ArrayList<>(); for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) { String attrName = tsKvProto.getKv().getKey(); - if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) { - newFirmwareTitle = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { - newFirmwareVersion = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_TAG.equals(attrName)) { - newFirmwareTag = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { - newFirmwareUrl = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { - newSoftwareTitle = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { - newSoftwareVersion = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TAG.equals(attrName)) { - newSoftwareTag = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) { - newSoftwareUrl = getStrValue(tsKvProto); - } else if (!DefaultLwM2MOtaUpdateService.FIRMWARE_SIZE.equals(attrName) && - !DefaultLwM2MOtaUpdateService.FIRMWARE_CHECKSUM.equals(attrName) && - !DefaultLwM2MOtaUpdateService.FIRMWARE_CHECKSUM_ALGORITHM.equals(attrName) && - !DefaultLwM2MOtaUpdateService.SOFTWARE_SIZE.equals(attrName) && - !DefaultLwM2MOtaUpdateService.SOFTWARE_CHECKSUM.equals(attrName) && - !DefaultLwM2MOtaUpdateService.SOFTWARE_CHECKSUM_ALGORITHM.equals(attrName)){ + if (attrName.contains(OtaPackageType.FIRMWARE.getKeyPrefix()) || attrName.contains(OtaPackageType.SOFTWARE.getKeyPrefix())) { + if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) { + newFirmwareTitle = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { + newFirmwareVersion = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_TAG.equals(attrName)) { + newFirmwareTag = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { + newFirmwareUrl = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { + newSoftwareTitle = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { + newSoftwareVersion = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TAG.equals(attrName)) { + newSoftwareTag = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) { + newSoftwareUrl = getStrValue(tsKvProto); + } + } else { otherAttributes.add(tsKvProto); } } From 8df2e83eddad6fdf31ff16243483086773297332 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 15 Sep 2021 10:55:07 +0300 Subject: [PATCH 18/21] Lwm2m test OTA fix bug. Object 9, 5 --- .../transport/TransportSqlTestSuite.java | 16 ++++---- .../LwM2mTransportServerHelperTest.java | 2 +- .../secure/LwM2MBootstrapSecurityStore.java | 8 ++-- .../LwM2mDefaultBootstrapSessionManager.java | 2 +- .../lwm2m/server/LwM2mServerListener.java | 2 +- .../DefaultLwM2MAttributesService.java | 38 +++++++++++-------- .../lwm2m/server/client/LwM2mClient.java | 12 +++--- .../server/client/LwM2mClientContextImpl.java | 6 +-- .../AbstractTbLwM2MRequestCallback.java | 2 +- .../DefaultLwM2mDownlinkMsgHandler.java | 10 ++--- .../lwm2m/server/downlink/HasVersionedId.java | 2 +- .../server/downlink/HasVersionedIds.java | 2 +- .../TbLwM2MCancelAllObserveCallback.java | 2 +- .../TbLwM2MCancelObserveCallback.java | 2 +- .../downlink/TbLwM2MTargetedCallback.java | 2 +- .../log/DefaultLwM2MTelemetryLogService.java | 2 +- .../ota/DefaultLwM2MOtaUpdateService.java | 10 +---- .../rpc/DefaultLwM2MRpcRequestHandler.java | 9 +---- .../server/rpc/RpcReadResponseCallback.java | 2 +- .../RpcReadResponseCompositeCallback.java | 2 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 14 +++---- .../{server => utils}/LwM2mTransportUtil.java | 24 +++++++++--- 22 files changed, 90 insertions(+), 81 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/{server => utils}/LwM2mTransportUtil.java (96%) diff --git a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java index 095321c184..5c26a65978 100644 --- a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java @@ -26,14 +26,14 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - "org.thingsboard.server.transport.*.rpc.sql.*Test", - "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", - "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", - "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", - "org.thingsboard.server.transport.*.attributes.request.sql.*Test", - "org.thingsboard.server.transport.*.claim.sql.*Test", - "org.thingsboard.server.transport.*.provision.sql.*Test", - "org.thingsboard.server.transport.lwm2m.*.sql.*Test" +// "org.thingsboard.server.transport.*.rpc.sql.*Test", +// "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", +// "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", +// "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", +// "org.thingsboard.server.transport.*.attributes.request.sql.*Test", +// "org.thingsboard.server.transport.*.claim.sql.*Test", +// "org.thingsboard.server.transport.*.provision.sql.*Test", + "org.thingsboard.server.transport.lwm2m.ota.sql.*Test" }) public class TransportSqlTestSuite { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelperTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelperTest.java index 15af28284e..fdccc2e933 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelperTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelperTest.java @@ -37,7 +37,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; class LwM2mTransportServerHelperTest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index fb6149a796..a83f006543 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -43,10 +43,10 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getBootstrapParametersFromThingsboard; import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP; @Slf4j diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java index 2e41d17635..7c6e157f83 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java @@ -39,7 +39,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; @Slf4j public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index edc408a78d..8c8624fcf3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -27,7 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle import java.util.Collection; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j public class LwM2mServerListener { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index c1c995e687..dfe5146017 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -26,7 +26,6 @@ import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResourceInstance; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; @@ -34,7 +33,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponse import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; @@ -57,10 +56,19 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromJson; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_TAG; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_URL; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_TAG; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_URL; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.compareAttNameKeyOta; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertMultiResourceValuesFromJson; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; @Slf4j @Service @@ -138,22 +146,22 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { List otherAttributes = new ArrayList<>(); for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) { String attrName = tsKvProto.getKv().getKey(); - if (attrName.contains(OtaPackageType.FIRMWARE.getKeyPrefix()) || attrName.contains(OtaPackageType.SOFTWARE.getKeyPrefix())) { - if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) { + if (compareAttNameKeyOta(attrName)) { + if (FIRMWARE_TITLE.equals(attrName)) { newFirmwareTitle = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { + } else if (FIRMWARE_VERSION.equals(attrName)) { newFirmwareVersion = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_TAG.equals(attrName)) { + } else if (FIRMWARE_TAG.equals(attrName)) { newFirmwareTag = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { + } else if (FIRMWARE_URL.equals(attrName)) { newFirmwareUrl = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { + } else if (SOFTWARE_TITLE.equals(attrName)) { newSoftwareTitle = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { + } else if (SOFTWARE_VERSION.equals(attrName)) { newSoftwareVersion = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TAG.equals(attrName)) { + } else if (SOFTWARE_TAG.equals(attrName)) { newSoftwareTag = getStrValue(tsKvProto); - } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) { + } else if (SOFTWARE_URL.equals(attrName)) { newSoftwareUrl = getStrValue(tsKvProto); } } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 8106871aee..048bc046aa 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -62,12 +62,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_OBJECT_VERSION_DEFAULT; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LWM2M_OBJECT_VERSION_DEFAULT; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getVerFromPathIdVerOrId; @Slf4j @EqualsAndHashCode(of = {"endpoint"}) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index ebf9c11155..71f4a0058a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -38,7 +38,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.session.LwM2MSessionManager; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MClientStore; @@ -57,8 +57,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import static org.eclipse.leshan.core.SecurityMode.NO_SEC; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getMsgException; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getMsgException; @Slf4j @Service diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java index 561b103277..c99a5ea2a1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN; @Slf4j public abstract class AbstractTbLwM2MRequestCallback implements DownlinkRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 0fdfb6c908..ddf6984eb8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -95,11 +95,11 @@ import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.STEP; import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createModelsDefault; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.createModelsDefault; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getVerFromPathIdVerOrId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.validateVersionedId; @Slf4j @Service diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java index fcbb195935..5e427fcb31 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; public interface HasVersionedId { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedIds.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedIds.java index 5ccef5ecea..a24789a410 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedIds.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedIds.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java index f90e0f8b97..d59ab9f16d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; @Slf4j public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java index b663215078..85accc5166 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; @Slf4j public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java index 92992d299c..52053093f0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java @@ -24,7 +24,7 @@ import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogServic import java.util.Arrays; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; @Slf4j public abstract class TbLwM2MTargetedCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java index 4e59eb5b44..ceaa163b63 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java @@ -22,7 +22,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; @Slf4j @Service diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index f25f826c30..6a9fc54971 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -74,8 +74,8 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDA import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j @Service @@ -87,16 +87,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); public static final String FIRMWARE_TAG = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TAG); public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL); - public static final String FIRMWARE_SIZE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE); - public static final String FIRMWARE_CHECKSUM_ALGORITHM = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM); - public static final String FIRMWARE_CHECKSUM = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM); public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION); public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); public static final String SOFTWARE_TAG = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TAG); public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); - public static final String SOFTWARE_SIZE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE); - public static final String SOFTWARE_CHECKSUM_ALGORITHM = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM); - public static final String SOFTWARE_CHECKSUM = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM); public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "tbfw"; public static final String SOFTWARE_UPDATE_COAP_RESOURCE = "tbsw"; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 57217290b5..3d032081ee 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -21,14 +21,10 @@ import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.request.ContentFormat; -import org.eclipse.leshan.core.request.ReadCompositeRequest; -import org.eclipse.leshan.core.response.ReadCompositeResponse; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -68,15 +64,14 @@ import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcReadRespon import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcWriteCompositeRequest; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; @Slf4j @Service diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java index 0a5fa70b5b..de033548fb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java @@ -24,7 +24,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.contentToString; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.contentToString; public class RpcReadResponseCallback, T extends ReadResponse> extends RpcLwM2MDownlinkCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java index 11f6849410..ea10ca90ca 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java @@ -25,7 +25,7 @@ import org.thingsboard.server.transport.lwm2m.server.rpc.RpcLwM2MDownlinkCallbac import java.util.Optional; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.contentToString; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.contentToString; public class RpcReadResponseCompositeCallback, T extends ReadCompositeResponse> extends RpcLwM2MDownlinkCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 84ac0277a3..bb8a74aaef 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -61,7 +61,7 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; @@ -106,12 +106,12 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertOtaUpdateValueToString; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_DELIVERY_METHOD; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_NAME_ID; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java similarity index 96% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java index 9648edc20f..69cee060ab 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.utils; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -45,7 +45,10 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.ota.OtaPackageKey; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.downlink.HasVersionedId; @@ -83,6 +86,7 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID; @@ -300,7 +304,8 @@ public class LwM2mTransportUtil { private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MUplinkMsgHandler serviceImpl, String target) { List attributeLists = new ArrayList<>(); - Map map = JacksonUtil.convertValue(params, new TypeReference<>() {}); + Map map = JacksonUtil.convertValue(params, new TypeReference<>() { + }); map.forEach((k, v) -> { if (StringUtils.trimToNull(v.toString()) != null) { Object attrValue = convertWriteAttributes(k, v, serviceImpl, target); @@ -316,9 +321,9 @@ public class LwM2mTransportUtil { } /** - * "UNSIGNED_INTEGER": // Number -> Integer Example: - * Alarm Timestamp [32-bit unsigned integer] - * Short Server ID, Object ID, Object Instance ID, Resource ID, Resource Instance ID + * "UNSIGNED_INTEGER": // Number -> Integer Example: + * Alarm Timestamp [32-bit unsigned integer] + * Short Server ID, Object ID, Object Instance ID, Resource ID, Resource Instance ID * "CORELINK": // String used in Attribute */ public static ResourceModel.Type equalsResourceTypeGetSimpleName(Object value) { @@ -349,7 +354,7 @@ public class LwM2mTransportUtil { } else { msgExceptionStr = client.isValidObjectVersion(request.getVersionedId()); } - if (!msgExceptionStr.isEmpty() ) { + if (!msgExceptionStr.isEmpty()) { throw new IllegalArgumentException(msgExceptionStr); } } @@ -531,4 +536,11 @@ public class LwM2mTransportUtil { return new StaticModel(ObjectLoader.loadDefault()); } + public static boolean compareAttNameKeyOta(String attrName) { + for (OtaPackageKey value : OtaPackageKey.values()) { + if (getAttributeKey(OtaPackageType.FIRMWARE, value).equals(attrName)) return true; + if (getAttributeKey(OtaPackageType.SOFTWARE, value).equals(attrName)) return true; + } + return false; + } } From 565cc29cbbb4c6a39d43d79f99f293add14461d6 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 15 Sep 2021 14:25:00 +0300 Subject: [PATCH 19/21] Lwm2m Fix bug Write, Read - if Not_Found --- .../transport/TransportSqlTestSuite.java | 16 ++--- .../DefaultLwM2mDownlinkMsgHandler.java | 1 + .../downlink/TbLwM2MTargetedCallback.java | 18 +++++- .../TbLwM2MWriteResponseCallback.java | 4 +- ...TbLwM2MWriteResponseCompositeCallback.java | 2 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 60 ++++++++++--------- .../server/uplink/LwM2mUplinkMsgHandler.java | 4 +- 7 files changed, 63 insertions(+), 42 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java index 5c26a65978..095321c184 100644 --- a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java @@ -26,14 +26,14 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ -// "org.thingsboard.server.transport.*.rpc.sql.*Test", -// "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", -// "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", -// "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", -// "org.thingsboard.server.transport.*.attributes.request.sql.*Test", -// "org.thingsboard.server.transport.*.claim.sql.*Test", -// "org.thingsboard.server.transport.*.provision.sql.*Test", - "org.thingsboard.server.transport.lwm2m.ota.sql.*Test" + "org.thingsboard.server.transport.*.rpc.sql.*Test", + "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", + "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", + "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", + "org.thingsboard.server.transport.*.attributes.request.sql.*Test", + "org.thingsboard.server.transport.*.claim.sql.*Test", + "org.thingsboard.server.transport.*.provision.sql.*Test", + "org.thingsboard.server.transport.lwm2m.*.sql.*Test" }) public class TransportSqlTestSuite { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index ddf6984eb8..309d456663 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.LwM2m; +import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.LwM2mModel; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java index 52053093f0..c14527e3b1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java @@ -16,7 +16,9 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.util.Hex; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @@ -24,6 +26,7 @@ import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogServic import java.util.Arrays; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; @Slf4j @@ -49,11 +52,22 @@ public abstract class TbLwM2MTargetedCallback extends AbstractTbLwM2MReque //TODO convert camelCase to "camel case" using .split("(?= ResponseCode.BAD_REQUEST.getCode()) { + log.error("[{}] [{}] [{}] failed to process successful response [{}] ", client.getEndpoint(), requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr); + logService.log(client, String.format("[%s]: %s [%s] failed to process successful. Result: %s", LOG_LWM2M_ERROR, requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr)); + } + else { + log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId != null ? versionedId : versionedIds, responseStr); + logService.log(client, String.format("[%s]: %s [%s] successful. Result: %s", LOG_LWM2M_INFO, requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr)); } - log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId != null ? versionedId : versionedIds, responseStr); - logService.log(client, String.format("[%s]: %s [%s] successful. Result: [%s]", LOG_LWM2M_INFO, requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr)); } private String responseToString (ReadResponse response) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java index 4746077f19..a452c3b187 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +@Slf4j public class TbLwM2MWriteResponseCallback extends TbLwM2MUplinkTargetedCallback { public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { @@ -30,7 +32,7 @@ public class TbLwM2MWriteResponseCallback extends TbLwM2MUplinkTargetedCallback< @Override public void onSuccess(WriteRequest request, WriteResponse response) { super.onSuccess(request, response); - handler.onWriteResponseOk(client, versionedId, request); + handler.onWriteResponseOk(client, versionedId, request, response.getCode().getCode()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MWriteResponseCompositeCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MWriteResponseCompositeCallback.java index 9dccc98185..6d5de06e90 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MWriteResponseCompositeCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MWriteResponseCompositeCallback.java @@ -31,7 +31,7 @@ public class TbLwM2MWriteResponseCompositeCallback extends TbLwM2MUplinkTargeted @Override public void onSuccess(WriteCompositeRequest request, WriteCompositeResponse response) { super.onSuccess(request, response); - handler.onWriteCompositeResponseOk(client, request); + handler.onWriteCompositeResponseOk(client, request, response.getCode().getCode()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index bb8a74aaef..b6f8095b60 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -21,6 +21,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mMultipleResource; @@ -61,7 +62,6 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; -import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; @@ -86,6 +86,7 @@ import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.session.LwM2MSessionManager; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; @@ -106,12 +107,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertOtaUpdateValueToString; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_DELIVERY_METHOD; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_NAME_ID; @@ -123,6 +118,12 @@ import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaU import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_VER_ID; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertOtaUpdateValueToString; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.fromVersionedIdToObjectId; @Slf4j @@ -316,13 +317,13 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl if (objectModelVersion != null) { if (response.getContent() instanceof LwM2mObject) { LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); - this.updateObjectResourceValue(lwM2MClient, lwM2mObject, path); + this.updateObjectResourceValue(lwM2MClient, lwM2mObject, path, response.getCode().getCode()); } else if (response.getContent() instanceof LwM2mObjectInstance) { LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent(); - this.updateObjectInstanceResourceValue(lwM2MClient, lwM2mObjectInstance, path); + this.updateObjectInstanceResourceValue(lwM2MClient, lwM2mObjectInstance, path, response.getCode().getCode()); } else if (response.getContent() instanceof LwM2mResource) { LwM2mResource lwM2mResource = (LwM2mResource) response.getContent(); - this.updateResourcesValue(lwM2MClient, lwM2mResource, path, Mode.UPDATE); + this.updateResourcesValue(lwM2MClient, lwM2mResource, path, Mode.UPDATE, response.getCode().getCode()); } } if (clientContext.awake(lwM2MClient)) { @@ -341,11 +342,11 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl response.getContent().forEach((k, v) -> { if (v != null) { if (v instanceof LwM2mObject) { - this.updateObjectResourceValue(lwM2MClient, (LwM2mObject) v, k.toString()); + this.updateObjectResourceValue(lwM2MClient, (LwM2mObject) v, k.toString(), response.getCode().getCode()); } else if (v instanceof LwM2mObjectInstance) { - this.updateObjectInstanceResourceValue(lwM2MClient, (LwM2mObjectInstance) v, k.toString()); + this.updateObjectInstanceResourceValue(lwM2MClient, (LwM2mObjectInstance) v, k.toString(), response.getCode().getCode()); } else if (v instanceof LwM2mResource) { - this.updateResourcesValue(lwM2MClient, (LwM2mResource) v, k.toString(), Mode.UPDATE); + this.updateResourcesValue(lwM2MClient, (LwM2mResource) v, k.toString(), Mode.UPDATE, response.getCode().getCode()); } } }); @@ -511,19 +512,19 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(logService, client, versionedId)); } - private void updateObjectResourceValue(LwM2mClient client, LwM2mObject lwM2mObject, String pathIdVer) { + private void updateObjectResourceValue(LwM2mClient client, LwM2mObject lwM2mObject, String pathIdVer, int code) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObject.getInstances().forEach((instanceId, instance) -> { String pathInstance = pathIds.toString() + "/" + instanceId; - this.updateObjectInstanceResourceValue(client, instance, pathInstance); + this.updateObjectInstanceResourceValue(client, instance, pathInstance, code); }); } - private void updateObjectInstanceResourceValue(LwM2mClient client, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) { + private void updateObjectInstanceResourceValue(LwM2mClient client, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer, int code) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { String pathRez = pathIds.toString() + "/" + resourceId; - this.updateResourcesValue(client, resource, pathRez, Mode.UPDATE); + this.updateResourcesValue(client, resource, pathRez, Mode.UPDATE, code); }); } @@ -538,7 +539,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl * @param path - resource * @param mode - Replace, Update */ - private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path, Mode mode) { + private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path, Mode mode, int code) { Registration registration = lwM2MClient.getRegistration(); if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider(), mode)) { if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, registration))) { @@ -564,7 +565,9 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl } else if (path.equals(convertObjectIdToVersionedId(SW_RESULT_ID, registration))) { otaService.onCurrentSoftwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } - this.updateAttrTelemetry(registration, Collections.singleton(path)); + if (ResponseCode.BAD_REQUEST.getCode() > code) { + this.updateAttrTelemetry(registration, Collections.singleton(path)); + } } else { log.error("Fail update path [{}] Resource [{}]", path, lwM2mResource); } @@ -708,38 +711,39 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl } @Override - public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request) { + public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request, int code) { if (request.getNode() instanceof LwM2mResource) { - this.updateResourcesValue(client, ((LwM2mResource) request.getNode()), path, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE); - clientContext.update(client); + this.updateResourcesValue(client, ((LwM2mResource) request.getNode()), path, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); } else if (request.getNode() instanceof LwM2mObjectInstance) { ((LwM2mObjectInstance) request.getNode()).getResources().forEach((resId, resource) -> { - this.updateResourcesValue(client, resource, path + "/" + resId, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE); + this.updateResourcesValue(client, resource, path + "/" + resId, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); }); + } + if (request.getNode() instanceof LwM2mResource || request.getNode() instanceof LwM2mObjectInstance) { clientContext.update(client); } } + @Override public void onCreateResponseOk(LwM2mClient client, String path, CreateRequest request) { if (request.getObjectInstances() != null && request.getObjectInstances().size() > 0) { - request.getObjectInstances().forEach( instance -> + request.getObjectInstances().forEach(instance -> instance.getResources() ); -// this.updateResourcesValue(client, ((LwM2mResource) request.getNode()), path, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE); clientContext.update(client); } } @Override - public void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request) { + public void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request, int code) { log.trace("ReadCompositeResponse: [{}]", request.getNodes()); request.getNodes().forEach((k, v) -> { if (v instanceof LwM2mSingleResource) { - this.updateResourcesValue(client, (LwM2mResource) v, k.toString(), Mode.REPLACE); + this.updateResourcesValue(client, (LwM2mResource) v, k.toString(), Mode.REPLACE, code); } else { LwM2mResourceInstance resourceInstance = (LwM2mResourceInstance) v; LwM2mMultipleResource multipleResource = new LwM2mMultipleResource(v.getId(), resourceInstance.getType(), resourceInstance); - this.updateResourcesValue(client, multipleResource, k.toString(), Mode.REPLACE); + this.updateResourcesValue(client, multipleResource, k.toString(), Mode.REPLACE, code); } }); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 0749ab34f6..af0d349687 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -55,11 +55,11 @@ public interface LwM2mUplinkMsgHandler { void onAwakeDev(Registration registration); - void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request); + void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request, int code); void onCreateResponseOk(LwM2mClient client, String path, CreateRequest request); - void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request); + void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request, int code); void onToTransportUpdateCredentials(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); From e2b3f0f742a7918f4ed232189c888cf1a9305007 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 15 Sep 2021 19:49:30 +0300 Subject: [PATCH 20/21] Lwm2m Fix bug Bootstrap Security URI --- .../bootstrap/secure/LwM2MBootstrapSecurityStore.java | 8 ++++++++ .../secure/LwM2mDefaultBootstrapSessionManager.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index a83f006543..c59f7467a9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -160,7 +160,15 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { BootstrapConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); lwM2MBootstrapConfig.servers = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class); LwM2MServerBootstrap profileServerBootstrap = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class); + if (SecurityMode.NO_SEC != profileServerBootstrap.getSecurityMode() && profileServerBootstrap != null) { + profileServerBootstrap.setSecurityHost( profileServerBootstrap.getHost()); + profileServerBootstrap.setSecurityPort( profileServerBootstrap.getPort()); + } LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); + if (SecurityMode.NO_SEC != profileLwm2mServer.getSecurityMode() && profileLwm2mServer != null) { + profileLwm2mServer.setSecurityHost(profileLwm2mServer.getHost()); + profileLwm2mServer.setSecurityPort(profileLwm2mServer.getPort()); + } UUID sessionUUiD = UUID.randomUUID(); TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); bsSessions.put(store.getEndpoint(), sessionInfo); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java index 7c6e157f83..da5e8db695 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java @@ -218,7 +218,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession } private void sendLogs (String endpointName, String logMsg) { - log.trace(logMsg); + log.info(logMsg); transportService.log(((LwM2MBootstrapSecurityStore) bsSecurityStore).getSessionByEndpoint(endpointName), logMsg); } } From b297ab231cd20dfe31a6b49b30f6f1dfff67207d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 16 Sep 2021 08:43:28 +0300 Subject: [PATCH 21/21] Lwm2m Fix bug create new Device with X509 Trust / update credential to X509 trust --- .../org/thingsboard/server/common/data/StringUtils.java | 8 ++++++++ .../server/dao/device/DeviceCredentialsServiceImpl.java | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java index cbe7094715..6e96893c22 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java @@ -21,7 +21,15 @@ public class StringUtils { return source == null || source.isEmpty(); } + public static boolean isBlank(String source) { + return source == null || source.isEmpty() || source.trim().isEmpty(); + } + public static boolean isNotEmpty(String source) { return source != null && !source.isEmpty(); } + + public static boolean isNotBlank(String source) { + return source != null && !source.isEmpty() && !source.trim().isEmpty(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index f62d25dcec..969745ae31 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -183,7 +183,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen break; case X509: X509ClientCredentials x509Config = (X509ClientCredentials) clientCredentials; - if (x509Config.getCert() != null) { + if ((StringUtils.isNotBlank(x509Config.getCert()))) { String cert = EncryptionUtil.trimNewLines(x509Config.getCert()); String sha3Hash = EncryptionUtil.getSha3Hash(cert); x509Config.setCert(cert); @@ -272,7 +272,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen break; case X509: X509ClientCredentials x509CCredentials = (X509ClientCredentials) clientCredentials; - if (x509CCredentials.getCert() != null) { + if (StringUtils.isNotBlank(x509CCredentials.getCert())) { try { SecurityUtil.certificate.decode(Base64.decodeBase64(x509CCredentials.getCert())); } catch (Exception e) {