Skip to content

Commit

Permalink
Add autoIdForSecurityObject mode to bootstrap config.
Browse files Browse the repository at this point in the history
This aims to resolve issue raised at
OpenMobileAlliance/OMA_LwM2M_for_Developers#522
  • Loading branch information
sbernard31 committed Jun 25, 2021
1 parent 0d24d6d commit b181ee9
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,34 @@ public void bootstrap_create_2_bsserver() {
helper.waitForInconsistentStateAtClientSide(1);
}

@Test
public void bootstrap_with_auto_id_for_security_object() {
// Create DM Server without security & start it
helper.createServer();
helper.server.start();

// Create and start bootstrap server
helper.createBootstrapServer(null, helper.unsecuredBootstrapWithAutoID());
helper.bootstrapServer.start();

// Create Client with bootstrap server config at /0/10
helper.createClient(helper.withoutSecurityAndInstanceId(10), null);
helper.assertClientNotRegisterered();

// Start BS.
// Server will delete /0 but Client will not delete /0/10 instance (because bs server is not deletable)
// Then a new BS server will be written at /0/0
//
// So bootstrap should failed because 2 bs server in Security Object is not a valid state.
// see https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/523
helper.client.start();

// ensure bootstrap session succeed
helper.waitForBootstrapFinishedAtClientSide(1);
helper.waitForRegistrationAtServerSide(1);
helper.assertClientRegisterered();
}

@Test
public void bootstrapDeleteSecurity() {
// Create DM Server without security & start it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResp
return tasks;
} else {
lastDiscoverAnswer = (BootstrapDiscoverResponse) previousResponses.get(0);
return super.getTasks(session, previousResponses);
return super.getTasks(session, null);
}
}
};
Expand Down Expand Up @@ -355,24 +355,34 @@ public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
}

public BootstrapConfigStore unsecuredBootstrapStore() {
return unsecuredBootstrapStoreWithBsSecurityInstanceIdAt(0);
return unsecuredBootstrapStore(0, false);
}

public BootstrapConfigStore unsecuredBootstrapStoreWithBsSecurityInstanceIdAt(final int instanceId) {
public BootstrapConfigStore unsecuredBootstrapStoreWithBsSecurityInstanceIdAt(Integer instanceId) {
return unsecuredBootstrapStore(instanceId, false);
}

public BootstrapConfigStore unsecuredBootstrapWithAutoID() {
return unsecuredBootstrapStore(0, true);
}

public BootstrapConfigStore unsecuredBootstrapStore(final Integer bsInstanceId, final boolean autoId) {
return new BootstrapConfigStore() {

@Override
public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSession session) {

BootstrapConfig bsConfig = new BootstrapConfig();

bsConfig.autoIdForSecurityObject = autoId;

// security for BS server
ServerSecurity bsSecurity = new ServerSecurity();
bsSecurity.bootstrapServer = true;
bsSecurity.uri = "coap://" + bootstrapServer.getUnsecuredAddress().getHostString() + ":"
+ bootstrapServer.getUnsecuredAddress().getPort();
bsSecurity.securityMode = SecurityMode.NO_SEC;
bsConfig.security.put(instanceId, bsSecurity);
bsConfig.security.put(bsInstanceId, bsSecurity);

// security for DM server
ServerSecurity dmSecurity = new ServerSecurity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.leshan.core.CertificateUsage;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.BootstrapDiscoverRequest;

/**
* A client configuration to apply to a device during a bootstrap session.
Expand All @@ -43,6 +44,20 @@
*/
public class BootstrapConfig {

/**
* If activated, bootstrap server will start the bootstrap session with a {@link BootstrapDiscoverRequest} to know
* what is the Security(object 0) instance ID of bootstrap server on the device, then it will use this information
* to automatically attribute IDs to {@link #security} instance. (meaning that ID defined in {@link #security} will
* not be used)
* <p>
* This can be useful because bootstrap server can not be deleted and only 1 Bootstrap server can be present in
* Security Object. <br>
* See <a href=
* "https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/522">OMA_LwM2M_for_Developers#522</a>for
* more details .
*/
public boolean autoIdForSecurityObject = false;

/**
* List of LWM2M path to delete.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,26 @@
*******************************************************************************/
package org.eclipse.leshan.server.bootstrap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.eclipse.leshan.core.Link;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.BootstrapDiscoverRequest;
import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An implementation of {@link BootstrapTaskProvider} which use a {@link BootstrapConfigStore} to know which requests to
* send during a {@link BootstrapSession}.
*/
public class BootstrapConfigStoreTaskProvider implements BootstrapTaskProvider {

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

private BootstrapConfigStore store;

public BootstrapConfigStoreTaskProvider(BootstrapConfigStore store) {
Expand All @@ -39,18 +48,73 @@ public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResp
if (config == null)
return null;

Tasks tasks = new Tasks();
// create requests from config
tasks.requestsToSend = BootstrapUtil.toRequests(config, session.getContentFormat());
if (previousResponse == null && shouldStartWithDiscover(config)) {
Tasks tasks = new Tasks();
tasks.requestsToSend = new ArrayList<>(1);
tasks.requestsToSend.add(new BootstrapDiscoverRequest());
tasks.last = false;
return tasks;
} else {
Tasks tasks = new Tasks();

// handle bootstrap discover response
if (previousResponse != null) {
BootstrapDiscoverResponse response = (BootstrapDiscoverResponse) previousResponse.get(0);
if (!response.isSuccess()) {
LOG.warn(
"Bootstrap Discover return error {} : unable to continue bootstrap session with autoIdForSecurityObject mode. {}",
response, session);
return null;
}

Integer bootstrapServerInstanceId = findBootstrapServerInstanceId(response.getObjectLinks());
if (bootstrapServerInstanceId == null) {
LOG.warn(
"Unable to find bootstrap server instance in Security Object (0) in response {}: unable to continue bootstrap session with autoIdForSecurityObject mode. {}",
response, session);
return null;
}

// create requests from config
tasks.requestsToSend = BootstrapUtil.toRequests(config, session.getContentFormat(),
bootstrapServerInstanceId);
} else {
// create requests from config
tasks.requestsToSend = BootstrapUtil.toRequests(config, session.getContentFormat());

}

// We add model for Security(0), Server(0) and ACL(2) which are the only one supported by BootstrapConfig
// We use default 1.0 model as currently BootstrapConfig support only this model version (which should be
// compatible with models of LWM2M v1.1 but without new resources)
tasks.supportedObjects = new HashMap<>();
tasks.supportedObjects.put(0, "1.0");
tasks.supportedObjects.put(1, "1.0");
tasks.supportedObjects.put(2, "1.0");
// We add model for Security(0), Server(0) and ACL(2) which are the only one supported by BootstrapConfig
// We use default 1.0 model as currently BootstrapConfig support only this model version (which should be
// compatible with models of LWM2M v1.1 but without new resources)
tasks.supportedObjects = new HashMap<>();
tasks.supportedObjects.put(0, "1.0");
tasks.supportedObjects.put(1, "1.0");
tasks.supportedObjects.put(2, "1.0");

return tasks;
}
}

protected boolean shouldStartWithDiscover(BootstrapConfig config) {
return config.autoIdForSecurityObject;
}

return tasks;
protected Integer findBootstrapServerInstanceId(Link[] objectLinks) {
for (Link link : objectLinks) {
if (link.getUrl().startsWith("/0/")) {
try {
LwM2mPath path = new LwM2mPath(link.getUrl());
if (path.isObjectInstance()) {
if (!link.getAttributes().containsKey("ssid"))
return path.getObjectInstanceId();
}
} catch (Exception e) {
// ignore if this is not a LWM2M path
LOG.warn("Invalid LwM2MPath starting by \"/0/\"");
}
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.eclipse.leshan.core.LwM2mId;
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
Expand Down Expand Up @@ -149,4 +150,34 @@ public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests
}
return (requests);
}

public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(BootstrapConfig bootstrapConfig,
ContentFormat contentFormat, int bootstrapServerID) {
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = new ArrayList<>();
// handle delete
for (String path : bootstrapConfig.toDelete) {
requests.add(new BootstrapDeleteRequest(path));
}
// handle security
int id = 0;
for (ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) {
if (security.bootstrapServer) {
requests.add(toWriteRequest(bootstrapServerID, security, contentFormat));
} else {
if (id == bootstrapServerID)
id++;
requests.add(toWriteRequest(id, security, contentFormat));
id++;
}
}
// handle server
for (Entry<Integer, ServerConfig> server : bootstrapConfig.servers.entrySet()) {
requests.add(toWriteRequest(server.getKey(), server.getValue(), contentFormat));
}
// handle acl
for (Entry<Integer, ACLConfig> acl : bootstrapConfig.acls.entrySet()) {
requests.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat));
}
return (requests);
}
}

0 comments on commit b181ee9

Please sign in to comment.