Merge pull request #5631 from thingsboard/lwm2m_include_bootstrap_update

[3.3.3] lwm2m - includes bootstrap update
This commit is contained in:
Yevhen Bondarenko 2021-11-29 11:15:43 +02:00 committed by GitHub
commit 2eb16eac01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 129 additions and 3 deletions

View File

@ -82,6 +82,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrapServerUpdateEnable\": true,\n" +
" \"bootstrap\": [\n" +
" {\n" +
" \"host\": \"0.0.0.0\",\n" +

View File

@ -92,6 +92,7 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrapServerUpdateEnable\": true,\n" +
" \"bootstrap\": [\n" +
" {\n" +
" \"host\": \"0.0.0.0\",\n" +

View File

@ -142,6 +142,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrapServerUpdateEnable\": true,\n" +
" \"bootstrap\": [\n" +
" {\n" +
" \"host\": \"0.0.0.0\",\n" +

View File

@ -24,6 +24,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
@ -36,6 +37,7 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
@ -75,6 +77,18 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
BootstrapConfig bsConfigNew = store.getBootstrapConfig();
if (bsConfigNew != null) {
try {
boolean bootstrapServerUpdateEnable = ((Lwm2mDeviceProfileTransportConfiguration)store.getDeviceProfile().getProfileData().getTransportConfiguration()).isBootstrapServerUpdateEnable();
if (!bootstrapServerUpdateEnable) {
Optional<Map.Entry<Integer, BootstrapConfig.ServerSecurity>> securities = bsConfigNew.security.entrySet().stream().filter(sec -> ((BootstrapConfig.ServerSecurity)sec.getValue()).bootstrapServer==true).findAny();
if (securities.isPresent()) {
bsConfigNew.security.entrySet().remove(securities.get());
int serverSortId = securities.get().getValue().serverId;
Optional<Map.Entry<Integer, BootstrapConfig.ServerConfig>> serverConfigs = bsConfigNew.servers.entrySet().stream().filter(serv -> ((BootstrapConfig.ServerConfig)serv.getValue()).shortId==serverSortId).findAny();
if (serverConfigs.isPresent()) {
bsConfigNew.servers.entrySet().remove(serverConfigs.get());
}
}
}
for (String config : bootstrapConfigStore.getAll().keySet()) {
if (config.equals(endPoint)) {
bootstrapConfigStore.remove(config);

View File

@ -0,0 +1,83 @@
/**
* 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.bootstrap.store;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.ConfigurationChecker;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
import java.util.Map;
public class LwM2MConfigurationChecker extends ConfigurationChecker {
@Override
public void verify(BootstrapConfig config) throws InvalidConfigurationException {
// check security configurations
for (Map.Entry<Integer, BootstrapConfig.ServerSecurity> e : config.security.entrySet()) {
BootstrapConfig.ServerSecurity sec = e.getValue();
// checks security config
switch (sec.securityMode) {
case NO_SEC:
checkNoSec(sec);
break;
case PSK:
checkPSK(sec);
break;
case RPK:
checkRPK(sec);
break;
case X509:
checkX509(sec);
break;
case EST:
throw new InvalidConfigurationException("EST is not currently supported.", e);
}
validateMandatoryField(sec);
}
// does each server have a corresponding security entry?
validateOneSecurityByServer(config);
}
protected void validateOneSecurityByServer(BootstrapConfig config) throws InvalidConfigurationException {
for (Map.Entry<Integer, BootstrapConfig.ServerConfig> e : config.servers.entrySet()) {
BootstrapConfig.ServerConfig srvCfg = e.getValue();
// shortId checks
if (srvCfg.shortId == 0) {
throw new InvalidConfigurationException("short ID must not be 0");
}
// look for security entry
BootstrapConfig.ServerSecurity security = getSecurityEntry(config, srvCfg.shortId);
if (security == null) {
throw new InvalidConfigurationException("no security entry for server instance: " + e.getKey());
}
}
}
protected static BootstrapConfig.ServerSecurity getSecurityEntry(BootstrapConfig config, int shortId) {
for (Map.Entry<Integer, BootstrapConfig.ServerSecurity> es : config.security.entrySet()) {
if (es.getValue().serverId == shortId) {
return es.getValue();
}
}
return null;
}
}

View File

@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.ConfigurationChecker;
import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@ -36,6 +37,7 @@ public class LwM2MInMemoryBootstrapConfigStore extends InMemoryBootstrapConfigSt
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
protected final ConfigurationChecker configChecker = new LwM2MConfigurationChecker();
@Override
public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {
@ -73,6 +75,26 @@ public class LwM2MInMemoryBootstrapConfigStore extends InMemoryBootstrapConfigSt
}
public void addToStore(String endpoint, BootstrapConfig config) throws InvalidConfigurationException {
super.add(endpoint, config);
configChecker.verify(config);
// Check PSK identity uniqueness for bootstrap server:
PskByServer pskToAdd = getBootstrapPskIdentity(config);
if (pskToAdd != null) {
BootstrapConfig existingConfig = bootstrapByPskId.get(pskToAdd);
if (existingConfig != null) {
// check if this config will be replace by the new one.
BootstrapConfig previousConfig = bootstrapByEndpoint.get(endpoint);
if (previousConfig != existingConfig) {
throw new InvalidConfigurationException(
"Psk identity [%s] already used for this bootstrap server [%s]", pskToAdd.identity,
pskToAdd.serverUrl);
}
}
}
bootstrapByEndpoint.put(endpoint, config);
if (pskToAdd != null) {
bootstrapByPskId.put(pskToAdd, config);
}
}
}

View File

@ -420,7 +420,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
} else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) {
List<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap();
validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations);
validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations,
((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).isBootstrapServerUpdateEnable());
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) {
validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential);
}
@ -707,11 +708,14 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
}
private void validateLwm2mServersConfigOfBootstrapForClient(List<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations) {
private void validateLwm2mServersConfigOfBootstrapForClient(List<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations, boolean isBootstrapServerUpdateEnable) {
Set <String> uris = new HashSet<>();
Set <Integer> shortServerIds = new HashSet<>();
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) {
AbstractLwM2MBootstrapServerCredential serverConfig = (AbstractLwM2MBootstrapServerCredential) bootstrapServerCredential;
if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) {
throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + "." );
}
String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server" + " shortServerId: " + serverConfig.getShortServerId() + ":";
if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) {
throw new DeviceCredentialsValidationException(server + " ShortServerId must not be less than 1 and more than 65534!");