Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Auto ID for Bootstrap Server Security Object(0) instance #1028

Merged
merged 10 commits into from
Jun 25, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder;
import org.eclipse.leshan.client.LwM2mClient;
import org.eclipse.leshan.client.RegistrationUpdateHandler;
import org.eclipse.leshan.client.bootstrap.BootstrapConsistencyChecker;
import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
import org.eclipse.leshan.client.bootstrap.DefaultBootstrapConsistencyChecker;
import org.eclipse.leshan.client.californium.bootstrap.BootstrapResource;
import org.eclipse.leshan.client.californium.object.ObjectResource;
import org.eclipse.leshan.client.californium.request.CaliforniumLwM2mRequestSender;
Expand Down Expand Up @@ -104,15 +106,17 @@ public LeshanClient(String endpoint, InetSocketAddress localAddress,
Map<String, String> additionalAttributes, Map<String, String> bsAdditionalAttributes,
LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder, ScheduledExecutorService sharedExecutor) {
this(endpoint, localAddress, objectEnablers, coapConfig, dtlsConfigBuilder, null, endpointFactory,
engineFactory, additionalAttributes, bsAdditionalAttributes, encoder, decoder, sharedExecutor);
engineFactory, new DefaultBootstrapConsistencyChecker(), additionalAttributes, bsAdditionalAttributes,
encoder, decoder, sharedExecutor);
}

/** @since 2.0 */
public LeshanClient(String endpoint, InetSocketAddress localAddress,
List<? extends LwM2mObjectEnabler> objectEnablers, NetworkConfig coapConfig, Builder dtlsConfigBuilder,
List<Certificate> trustStore, EndpointFactory endpointFactory, RegistrationEngineFactory engineFactory,
Map<String, String> additionalAttributes, Map<String, String> bsAdditionalAttributes,
LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder, ScheduledExecutorService sharedExecutor) {
BootstrapConsistencyChecker checker, Map<String, String> additionalAttributes,
Map<String, String> bsAdditionalAttributes, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder,
ScheduledExecutorService sharedExecutor) {

Validate.notNull(endpoint);
Validate.notEmpty(objectEnablers);
Expand All @@ -123,7 +127,7 @@ public LeshanClient(String endpoint, InetSocketAddress localAddress,
this.decoder = decoder;
this.encoder = encoder;
observers = createClientObserverDispatcher();
bootstrapHandler = createBoostrapHandler(objectTree);
bootstrapHandler = createBoostrapHandler(objectTree, checker);
endpointsManager = createEndpointsManager(localAddress, coapConfig, dtlsConfigBuilder, trustStore,
endpointFactory);
requestSender = createRequestSender(endpointsManager, sharedExecutor, encoder, objectTree.getModel());
Expand Down Expand Up @@ -160,8 +164,8 @@ public void onUnexpectedError(Throwable unexpectedError) {
return observer;
}

protected BootstrapHandler createBoostrapHandler(LwM2mObjectTree objectTree) {
return new BootstrapHandler(objectTree.getObjectEnablers());
protected BootstrapHandler createBoostrapHandler(LwM2mObjectTree objectTree, BootstrapConsistencyChecker checker) {
return new BootstrapHandler(objectTree.getObjectEnablers(), checker);
}

protected CoapServer createCoapServer(NetworkConfig coapConfig, ScheduledExecutorService sharedExecutor) {
Expand Down Expand Up @@ -304,6 +308,11 @@ public void triggerRegistrationUpdate(ServerIdentity server) {
engine.triggerRegistrationUpdate(server);
}

@Override
public boolean triggerClientInitiatedBootstrap(boolean deregister) {
return engine.triggerClientInitiatedBootstrap(deregister);
}

@Override
public SendResponse sendData(ServerIdentity server, ContentFormat format, List<String> paths, long timeoutInMs)
throws InterruptedException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder;
import org.eclipse.leshan.client.bootstrap.BootstrapConsistencyChecker;
import org.eclipse.leshan.client.bootstrap.DefaultBootstrapConsistencyChecker;
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
import org.eclipse.leshan.client.engine.RegistrationEngine;
import org.eclipse.leshan.client.engine.RegistrationEngineFactory;
Expand Down Expand Up @@ -71,11 +73,11 @@ public class LeshanClientBuilder {
private EndpointFactory endpointFactory;
private RegistrationEngineFactory engineFactory;
private Map<String, String> additionalAttributes;
private Map<String, String> bsAdditionalAttributes;

private ScheduledExecutorService executor;
private BootstrapConsistencyChecker bootstrapConsistencyChecker;

/** @since 1.1 */
protected Map<String, String> bsAdditionalAttributes;
private ScheduledExecutorService executor;

/**
* Creates a new instance for setting the configuration options for a {@link LeshanClient} instance.
Expand Down Expand Up @@ -217,6 +219,18 @@ public LeshanClientBuilder setBootstrapAdditionalAttributes(Map<String, String>
return this;
}

/**
* Set a {@link BootstrapConsistencyChecker} which is used to valid client state after a bootstrap session.
* <p>
* By default a {@link DefaultBootstrapConsistencyChecker} is used.
*
* @return the builder for fluent client creation.
*/
public LeshanClientBuilder setBootstrapConsistencyChecker(BootstrapConsistencyChecker checker) {
this.bootstrapConsistencyChecker = checker;
return this;
}

/**
* Set a shared executor. This executor will be used everywhere it is possible. This is generally used when you want
* to limit the number of thread to use or if you want to simulate a lot of clients sharing the same thread pool.
Expand Down Expand Up @@ -283,6 +297,9 @@ protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) {
}
};
}
if (bootstrapConsistencyChecker == null) {
bootstrapConsistencyChecker = new DefaultBootstrapConsistencyChecker();
}

// handle dtlsConfig
if (dtlsConfigBuilder == null) {
Expand Down Expand Up @@ -324,7 +341,8 @@ protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) {
}

return createLeshanClient(endpoint, localAddress, objectEnablers, coapConfig, dtlsConfigBuilder,
this.trustStore, endpointFactory, engineFactory, additionalAttributes, encoder, decoder, executor);
this.trustStore, endpointFactory, engineFactory, bootstrapConsistencyChecker, additionalAttributes,
bsAdditionalAttributes, encoder, decoder, executor);
}

/**
Expand All @@ -344,7 +362,9 @@ protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) {
* @param trustStore The optional trust store for verifying X.509 server certificates.
* @param endpointFactory The factory which will create the {@link CoapEndpoint}.
* @param engineFactory The factory which will create the {@link RegistrationEngine}.
* @param checker Used to check if client state is consistent after a bootstrap session.
* @param additionalAttributes Some extra (out-of-spec) attributes to add to the register request.
* @param bsAdditionalAttributes Some extra (out-of-spec) attributes to add to the bootstrap request.
* @param encoder used to encode request payload.
* @param decoder used to decode response payload.
* @param sharedExecutor an optional shared executor.
Expand All @@ -354,10 +374,11 @@ protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) {
protected LeshanClient createLeshanClient(String endpoint, InetSocketAddress localAddress,
List<? extends LwM2mObjectEnabler> objectEnablers, NetworkConfig coapConfig, Builder dtlsConfigBuilder,
List<Certificate> trustStore, EndpointFactory endpointFactory, RegistrationEngineFactory engineFactory,
Map<String, String> additionalAttributes, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder,
BootstrapConsistencyChecker checker, Map<String, String> additionalAttributes,
Map<String, String> bsAdditionalAttributes, LwM2mNodeEncoder encoder, LwM2mNodeDecoder decoder,
ScheduledExecutorService sharedExecutor) {
return new LeshanClient(endpoint, localAddress, objectEnablers, coapConfig, dtlsConfigBuilder, trustStore,
endpointFactory, engineFactory, additionalAttributes, bsAdditionalAttributes, encoder, decoder,
executor);
endpointFactory, engineFactory, checker, additionalAttributes, bsAdditionalAttributes, encoder, decoder,
sharedExecutor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ public interface LwM2mClient {
*/
void triggerRegistrationUpdate(ServerIdentity server);

/**
* Trigger a client initiated bootstrap.
*
* @param deregister True if client should deregister itself before to stop.
*
* @return true if the bootstrap can be initiated.
*/
boolean triggerClientInitiatedBootstrap(boolean deregister);

/**
* Send Data synchronously to a LWM2M Server.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2021 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.client.bootstrap;

import java.util.List;
import java.util.Map;

import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;

/**
* This class is responsible to check the consistency state of the client after at the end of a bootstrap session.
*/
public interface BootstrapConsistencyChecker {

/**
* check if current state of the client is consistent
*
* @param objectEnablers all objects supported by the client.
* @return <code>null</code> if the current state is consistent or a list of issues
*/
List<String> checkconfig(Map<Integer, LwM2mObjectEnabler> objectEnablers);

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*******************************************************************************/
package org.eclipse.leshan.client.bootstrap;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
Expand All @@ -40,11 +41,15 @@ public class BootstrapHandler {

private boolean bootstrapping = false;
private CountDownLatch bootstrappingLatch = new CountDownLatch(1);
// last session state (null means no error)
private volatile List<String> lastConsistencyError = null;

private final Map<Integer, LwM2mObjectEnabler> objects;
private BootstrapConsistencyChecker checker;

public BootstrapHandler(Map<Integer, LwM2mObjectEnabler> objectEnablers) {
public BootstrapHandler(Map<Integer, LwM2mObjectEnabler> objectEnablers, BootstrapConsistencyChecker checker) {
objects = objectEnablers;
this.checker = checker;
}

public synchronized SendableResponse<BootstrapFinishResponse> finished(ServerIdentity server,
Expand All @@ -54,7 +59,6 @@ public synchronized SendableResponse<BootstrapFinishResponse> finished(ServerIde
if (!server.isLwm2mBootstrapServer()) {
return new SendableResponse<>(BootstrapFinishResponse.badRequest("not from a bootstrap server"));
}
// TODO delete bootstrap server (see 5.2.5.2 Bootstrap Delete)

Runnable whenSent = new Runnable() {
@Override
Expand All @@ -63,7 +67,17 @@ public void run() {
}
};

return new SendableResponse<>(BootstrapFinishResponse.success(), whenSent);
// check consistency state of the client
lastConsistencyError = checker.checkconfig(objects);
if (lastConsistencyError == null) {
return new SendableResponse<>(BootstrapFinishResponse.success(), whenSent);
} else {
// TODO rollback configuration.
// see https://github.com/eclipse/leshan/issues/968
return new SendableResponse<>(BootstrapFinishResponse.notAcceptable(lastConsistencyError.toString()),
whenSent);
}

} else {
return new SendableResponse<>(BootstrapFinishResponse.badRequest("no pending bootstrap session"));
}
Expand Down Expand Up @@ -91,6 +105,7 @@ public synchronized boolean tryToInitSession() {
if (!bootstrapping) {
bootstrappingLatch = new CountDownLatch(1);
bootstrapping = true;
lastConsistencyError = null;
return true;
}
return false;
Expand All @@ -100,8 +115,15 @@ public synchronized boolean isBootstrapping() {
return bootstrapping;
}

public boolean waitBoostrapFinished(long timeInSeconds) throws InterruptedException {
return bootstrappingLatch.await(timeInSeconds, TimeUnit.SECONDS);
public boolean waitBoostrapFinished(long timeInSeconds) throws InterruptedException, InvalidStateException {
boolean finished = bootstrappingLatch.await(timeInSeconds, TimeUnit.SECONDS);
if (finished) {
if (lastConsistencyError != null) {
throw new InvalidStateException(
String.format("Invalid Bootstrap state : %s", lastConsistencyError.toString()));
}
}
return finished;
}

public synchronized void closeSession() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2021 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.client.bootstrap;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.servers.ServersInfoExtractor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A default implementation of {@link BootstrapConsistencyChecker}
*/
public class DefaultBootstrapConsistencyChecker implements BootstrapConsistencyChecker {

private static final Logger LOG = LoggerFactory.getLogger(DefaultBootstrapConsistencyChecker.class);

@Override
public List<String> checkconfig(Map<Integer, LwM2mObjectEnabler> objectEnablers) {
try {
ServersInfoExtractor.getInfo(objectEnablers, true);
} catch (RuntimeException e) {
LOG.debug(e.getMessage());
return Arrays.asList(e.getMessage());
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2021 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.client.bootstrap;

/**
* Raised if LWM2M client is in an invalid or inconsistent state.
*
*/
public class InvalidStateException extends Exception {
private static final long serialVersionUID = 1L;

public InvalidStateException() {
}

public InvalidStateException(String m) {
super(m);
}

public InvalidStateException(String m, Object... args) {
super(String.format(m, args));
}

public InvalidStateException(Throwable e) {
super(e);
}

public InvalidStateException(String m, Throwable e) {
super(m, e);
}

public InvalidStateException(Throwable e, String m, Object... args) {
super(String.format(m, args), e);
}
}
Loading