Skip to content

Commit

Permalink
#1112: Add SecurityStep to wizard to define how client authenticate.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Nov 30, 2021
1 parent 094ab55 commit 771b837
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ private static Server createJettyServer(LeshanBsServerDemoCLI cli, LeshanBootstr
root.setResourceBase(LeshanBootstrapServerDemo.class.getClassLoader().getResource("webapp").toExternalForm());
root.setParentLoaderPriority(true);

ServletHolder bsServletHolder = new ServletHolder(new BootstrapServlet(bsStore, securityStore));
ServletHolder bsServletHolder = new ServletHolder(new BootstrapServlet(bsStore));
root.addServlet(bsServletHolder, "/api/bootstrap/*");

ServletHolder serverServletHolder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,20 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.EnumSet;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerSecurity;
import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
import org.eclipse.leshan.server.bootstrap.demo.json.ByteArraySerializer;
import org.eclipse.leshan.server.bootstrap.demo.json.EnumSetDeserializer;
import org.eclipse.leshan.server.bootstrap.demo.json.EnumSetSerializer;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParseException;
Expand All @@ -50,8 +42,6 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;

import jline.internal.Log;

/**
* Servlet for REST API in charge of adding bootstrap information to the bootstrap server.
*/
Expand All @@ -61,11 +51,9 @@ public class BootstrapServlet extends HttpServlet {
private final ObjectMapper mapper;

private final EditableBootstrapConfigStore bsStore;
private final EditableSecurityStore securityStore;

public BootstrapServlet(EditableBootstrapConfigStore bsStore, EditableSecurityStore securityStore) {
public BootstrapServlet(EditableBootstrapConfigStore bsStore) {
this.bsStore = bsStore;
this.securityStore = securityStore;

mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Expand Down Expand Up @@ -123,40 +111,11 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
if (cfg == null) {
sendError(resp, HttpServletResponse.SC_BAD_REQUEST, "no content");
} else {
// try to add securityInfo if needed.
if (cfg.security != null) {
for (Map.Entry<Integer, BootstrapConfig.ServerSecurity> bsEntry : cfg.security.entrySet()) {
ServerSecurity value = bsEntry.getValue();
// Extract PSK security info
if (value.bootstrapServer) {
SecurityInfo securityInfo = null;
if (value.securityMode == SecurityMode.PSK) {
securityInfo = SecurityInfo.newPreSharedKeyInfo(endpoint,
new String(value.publicKeyOrId, StandardCharsets.UTF_8), value.secretKey);
}
// Extract RPK security info
else if (value.securityMode == SecurityMode.RPK) {
securityInfo = SecurityInfo.newRawPublicKeyInfo(endpoint,
SecurityUtil.publicKey.decode(value.publicKeyOrId));
}
// Extract X509 security info
else if (value.securityMode == SecurityMode.X509) {
securityInfo = SecurityInfo.newX509CertInfo(endpoint);
}
if (securityInfo != null) {
securityStore.add(securityInfo);
}
break;
}
}
}

// Add bootstrap config
bsStore.add(endpoint, cfg);
resp.setStatus(HttpServletResponse.SC_OK);
}
} catch (JsonParseException | InvalidConfigurationException | NonUniqueSecurityInfoException
| GeneralSecurityException e) {
} catch (JsonParseException | InvalidConfigurationException e) {
sendError(resp, HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
}
}
Expand All @@ -180,14 +139,6 @@ protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws

String endpoint = path[0];

// try to remove from securityStore
try {
securityStore.remove(endpoint, true);
} catch (RuntimeException e) {
// Best effort here we just try to remove, if it failed we try to remove bsconfig anyway.
Log.warn("unable to remove security info for client {}", endpoint, e);
}

// try to remove from bootstrap config store
if (bsStore.remove(endpoint) != null) {
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@
<strong>LWM2M Bootstrap Server</strong> to your LWM2M Client during the
bootstrap Session by writing an instance for object <code>/0</code>.
</p>
<p>
This data will also be used
<a
href="https://github.com/eclipse/leshan/issues/690#issuecomment-490949978"
target="_blank"
>to know how the client must connect to this server</a
>.
</p>
</v-card-text>
<v-form ref="form" :value="valid" @input="$emit('update:valid', $event)">
<server-input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
<v-card-title class="headline grey lighten-2">
<span>Add Client Configuration</span>
</v-card-title>

<!-- Form -->
<v-stepper v-model="currentStep">
<v-stepper-header>
Expand All @@ -31,10 +30,14 @@
</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="currentStep > 2" step="2">
LWM2M Server Configuration
Credentials
</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="currentStep > 3" step="3">
LWM2M Server Configuration
</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="currentStep > 4" step="4">
LWM2M Bootstrap Server Configuration
</v-stepper-step>
</v-stepper-header>
Expand All @@ -47,19 +50,26 @@
v-model="config.endpoint"
/>
</v-stepper-content>
<v-stepper-content step="2">
<server-step
<v-stepper-content step="2">
<security-step
ref="step2"
:valid.sync="valid[2]"
v-model="config.security"
/>
</v-stepper-content>
<v-stepper-content step="3">
<server-step
ref="step3"
:valid.sync="valid[3]"
v-model="config.dm"
:defaultNoSecValue="defval.dm.url.nosec"
:defaultSecureValue="defval.dm.url.sec"
/>
</v-stepper-content>
<v-stepper-content step="3">
<v-stepper-content step="4">
<bootstrap-server-step
ref="step3"
:valid.sync="valid[3]"
ref="step4"
:valid.sync="valid[4]"
v-model="config.bs"
:defaultNoSecValue="defval.bs.url.nosec"
:defaultSecureValue="defval.bs.url.sec"
Expand Down Expand Up @@ -98,21 +108,23 @@
Cancel
</v-btn>
</v-card-actions>

</v-card>
</v-dialog>
</template>
<script>
import { toHex, base64ToBytes } from "@leshan-server-core-demo/js/byteutils.js";
import EndpointStep from "./EndpointStep.vue";
import SecurityStep from "./SecurityStep.vue";
import ServerStep from "./ServerStep.vue";
import BootstrapServerStep from "./BootstrapServerStep.vue";

export default {
components: { EndpointStep, ServerStep, BootstrapServerStep },
components: { EndpointStep, SecurityStep, ServerStep, BootstrapServerStep },
props: { value: Boolean /*open/close dialog*/ },
data() {
return {
nbSteps: 3,
nbSteps: 4,
config: {}, // local state for current config
valid: [],
currentStep: 1,
Expand Down Expand Up @@ -163,6 +175,7 @@ export default {
// reset validation and set initial value when dialog opens
this.config = {
endpoint: null,
security: null,
dm: { mode: "no_sec" },
bs: { mode: "no_sec" },
};
Expand Down
20 changes: 5 additions & 15 deletions leshan-bsserver-demo/webapp/src/components/wizard/EndpointStep.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,14 @@
<v-card class="mb-12" elevation="0">
<v-card-text class="pb-0">
<p>
To allow a client to bootstrap to this server you need to create a
configuration for it.
To allow a <strong>LWM2M client</strong> to bootstrap to this server you
need to create a configuration for it.
</p>
<p>
This wizard is pretty limitted. It create a configuration which starts
This wizard is pretty limitted. It creates a configuration which starts
by deleting objects <code>/0</code> and <code>/1</code>, then write
instance for those objects for 1 LWM2M server and 1 LWM2M
BootstrapServer with provided data.
</p>
<p>
How the client is supposed to connect to this bootstrap server
<a
href="https://github.com/eclipse/leshan/issues/690#issuecomment-490949978"
target="_blank"
>is guessed</a
>
from the
<strong>LWM2M Bootstrap Server Configuration</strong>.
instances for those objects to add one LWM2M server and/or one LWM2M
Bootstrap Server.
</p>
</v-card-text>
<v-form ref="form" :value="valid" @input="$emit('update:valid', $event)">
Expand Down
90 changes: 90 additions & 0 deletions leshan-bsserver-demo/webapp/src/components/wizard/SecurityStep.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!-----------------------------------------------------------------------------
* 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.
----------------------------------------------------------------------------->
<template>
<v-card class="mb-12" elevation="0">
<v-card-text class="pb-0">
<p>
This step aims to define how your <strong>LWM2M client</strong> will be
authenticated by the <strong>LWM2M Bootstrap Server</strong>. <br />
By the default there is no authentication expected and so the client
should connect using any unsecured endpoint (e.g. using coap://
endpoint). This behavior is only acceptable because this server is just
a demo.
</p>
</v-card-text>
<v-form ref="form" :value="valid" @input="$emit('update:valid', !useDTLS || $event)">
<v-switch class="pl-5"
v-model="useDTLS"
@change="updateUseDTLS($event)"
label="Using (D)TLS"
></v-switch>
<security-info-input
v-show="useDTLS"
:mode="internalSecurityInfo.mode"
:details="internalSecurityInfo.details"
@update:mode="updateMode($event)"
@update:details="updateDetails($event)"
/>
</v-form>
</v-card>
</template>
<script>
import SecurityInfoInput from "@leshan-server-core-demo/components/security/SecurityInfoInput.vue";
export default {
components: { SecurityInfoInput },
props: {
value: Object, // Security Info
valid: Boolean, // validation state of the form
},
data() {
return {
useDTLS: false,
internalSecurityInfo: { mode: "psk", details: {} },
};
},
watch: {
value(v) {
if (!v) {
this.useDTLS = false;
this.internalSecurityInfo = { mode: "psk", details: {} };
} else {
this.useDTLS = true;
this.internalSecurityInfo = v;
}
},
},
methods: {
updateUseDTLS(useDTLS) {
if (useDTLS) {
this.$emit("input", this.internalSecurityInfo);
this.resetValidation();
this.$emit('update:valid', false);
} else {
this.$emit("input", null);
this.$emit('update:valid', true);
}
},
updateMode(mode) {
this.internalSecurityInfo.mode = mode;
this.$emit("input", this.internalSecurityInfo);
},
updateDetails(mode) {
this.internalSecurityInfo.details = mode;
this.$emit("input", this.internalSecurityInfo);
},
resetValidation() {
this.$refs.form.resetValidation();
},
},
};
</script>
Loading

0 comments on commit 771b837

Please sign in to comment.