diff --git a/examples/hawkbit-device-simulator/README.md b/examples/hawkbit-device-simulator/README.md index 610a1d256..869f80511 100644 --- a/examples/hawkbit-device-simulator/README.md +++ b/examples/hawkbit-device-simulator/README.md @@ -13,7 +13,7 @@ run org.eclipse.hawkbit.simulator.DeviceSimulator ## Notes -The simulator has user authentication enabled by default. Default credentials: +The simulator has user authentication enabled in **cloud profile**. Default credentials: * username : admin * passwd : admin @@ -29,8 +29,6 @@ The UI can be accessed via the URL: http://localhost:8083 ``` -`Basic Authentication Credentials are admin / admin` - ![](src/main/images/generateScreenshot.png) ![](src/main/images/updateProcessScreenshot.png) @@ -45,6 +43,10 @@ Optional parameters: * name : name prefix simulated devices (default: "dmfSimulated"), followed by counter * amount : number of simulated devices (default: 20, capped at: 4000) * tenant : in a multi-tenenat ready hawkBit installation (default: "DEFAULT") +* api : the API which should be used for the simulated device either `dmf` or `ddi` (default: "ddi") +* endpoint : URL which defines the hawkbit DDI base endpoint (deffault: "http://localhost:8080") +* polldelay : number in milliseconds of the delay when DDI simulated devices should poll the endpoint (default: "30") +* gatewaytoken : an hawkbit gateway token to be used in case hawkbit does not allow anonymous access for DDI devices (default: "") Example: for 20 simulated devices (default) @@ -56,3 +58,8 @@ Example: for 10 simulated devices that start with the name prefix "activeSim": ``` http://localhost:8083/start?amount=10&name=activeSim ``` + +Example: for 5 simulated devices that start with the name prefix "ddi" using the Direct Device Integration API (http): +``` +http://localhost:8083/start?amount=5&name=ddi?api=ddi +``` diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java index 6f94bd319..c1f358d89 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java @@ -8,9 +8,13 @@ */ package org.eclipse.hawkbit.simulator; +import java.net.MalformedURLException; +import java.net.URL; + import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; import org.eclipse.hawkbit.simulator.amqp.SpSenderService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -42,19 +46,49 @@ public class SimulationController { * the amount of devices to be created * @param tenant * the tenant to create the device to + * @param api + * the api-protocol to be used either {@code dmf} or {@code ddi} + * @param endpoint + * the URL endpoint to be used of the hawkbit-update-server for + * DDI devices + * @param pollDelay + * number of delay in milliseconds to delay polling of DDI + * devices + * @param gatewayToken + * the hawkbit-update-server gatwaytoken in case authentication + * is enforced in hawkbit * @return a response string that devices has been created + * @throws MalformedURLException */ @RequestMapping("/start") - String start(@RequestParam(value = "name", defaultValue = "dmfSimulated") final String name, + ResponseEntity start(@RequestParam(value = "name", defaultValue = "simulated") final String name, @RequestParam(value = "amount", defaultValue = "20") final int amount, - @RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) { + @RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant, + @RequestParam(value = "api", defaultValue = "dmf") final String api, + @RequestParam(value = "endpoint", defaultValue = "http://localhost:8080") final String endpoint, + @RequestParam(value = "polldelay", defaultValue = "30") final int pollDelay, + @RequestParam(value = "gatewaytoken", defaultValue = "") final String gatewayToken) + throws MalformedURLException { + + final Protocol protocol; + switch (api.toLowerCase()) { + case "dmf": + protocol = Protocol.DMF_AMQP; + break; + case "ddi": + protocol = Protocol.DDI_HTTP; + break; + default: + return ResponseEntity.badRequest().body("query param api only allows value of 'dmf' or 'ddi'"); + } for (int i = 0; i < amount; i++) { final String deviceId = name + i; - repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, Protocol.DMF_AMQP)); + repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint), + gatewayToken)); spSenderService.createOrUpdateThing(tenant, deviceId); } - return "Updated " + amount + " DMF connected targets!"; + return ResponseEntity.ok("Updated " + amount + " DMF connected targets!"); } } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java index 1400ec0e1..e4509e082 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java @@ -83,8 +83,8 @@ public class GenerateDialog extends Window { tf5.setIcon(FontAwesome.FLAG_O); tf5.setRequired(true); tf5.setVisible(false); - tf5.addValidator(new RegexpValidator( - "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "is not an URL")); + tf5.addValidator(new RegexpValidator("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", + "is not an URL")); final TextField tf6 = new TextField("gateway token", ""); tf6.setColumns(50); @@ -125,7 +125,8 @@ public class GenerateDialog extends Window { @Override public void buttonClick(final ClickEvent event) { try { - callback.okButton(tf1.getValue(), tf3.getValue(), Integer.valueOf(tf2.getValue().replace(".", "")), + callback.okButton(tf1.getValue(), tf3.getValue(), + Integer.valueOf(tf2.getValue().replace(".", "").replace(",", "")), Integer.valueOf(tf4.getValue().replace(".", "")), new URL(tf5.getValue()), tf6.getValue(), (Protocol) protocolGroup.getValue()); } catch (final NumberFormatException e) { diff --git a/examples/hawkbit-device-simulator/src/main/resources/application-cloud.properties b/examples/hawkbit-device-simulator/src/main/resources/application-cloud.properties new file mode 100644 index 000000000..3e3edc90c --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/resources/application-cloud.properties @@ -0,0 +1,28 @@ +# +# Copyright (c) 2015 Bosch Software Innovations GmbH and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# + +# SECURITY (SecurityProperties) +security.basic.enabled=true +security.user.name=${BASIC_USERNAME:admin} +security.user.password=${BASIC_PASSWORD:admin} +security.user.role=USER +security.require-ssl=false +security.enable-csrf=false +security.basic.enabled=true +security.basic.realm=DeviceSimulator +security.basic.path= /** +security.basic.authorize-mode=ROLE +security.filter-order=0 +security.headers.xss=false +security.headers.cache=false +security.headers.frame=false +security.headers.content-type=false +security.headers.hsts=all +security.sessions=stateless +security.ignored=/VAADIN/** \ No newline at end of file diff --git a/examples/hawkbit-device-simulator/src/main/resources/application.properties b/examples/hawkbit-device-simulator/src/main/resources/application.properties index 402f71bfe..56d0190a7 100644 --- a/examples/hawkbit-device-simulator/src/main/resources/application.properties +++ b/examples/hawkbit-device-simulator/src/main/resources/application.properties @@ -27,23 +27,5 @@ spring.rabbitmq.port=5672 spring.rabbitmq.dynamic=true spring.rabbitmq.listener.prefetch=100 -# SECURITY (SecurityProperties) -security.user.name=${BASIC_USERNAME:admin} -security.user.password=${BASIC_PASSWORD:admin} -security.user.role=USER -security.require-ssl=false -security.enable-csrf=false -security.basic.enabled=true -security.basic.realm=DeviceSimulator -security.basic.path= /** -security.basic.authorize-mode=ROLE -security.filter-order=0 -security.headers.xss=false -security.headers.cache=false -security.headers.frame=false -security.headers.content-type=false -security.headers.hsts=all -security.sessions=stateless -security.ignored=/VAADIN/** - +security.basic.enabled=false server.port=8083 diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java index b9bf45fd0..8cc056f15 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java @@ -10,8 +10,9 @@ package org.eclipse.hawkbit.security; import java.util.List; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; /** * The common properties for security. @@ -22,47 +23,108 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties public class SecurityProperties { - @Value("${hawkbit.server.controller.security.rp.cnHeader:X-Ssl-Client-Cn}") - private String rpCnHeader; + /** + * Inner class for reverse proxy configuration. + */ + @Component + @ConfigurationProperties("hawkbit.server.controller.security.rp") + public static class RpProperties { + private String cnHeader = "X-Ssl-Client-Cn"; + private String sslIssuerHashHeader = "X-Ssl-Issuer-Hash-%d"; + private List trustedIPs; - @Value("${hawkbit.server.controller.security.rp.sslIssuerHashHeader:X-Ssl-Issuer-Hash-%d}") - private String rpSslIssuerHashHeader; + /** + * @return the cnHeader + */ + public String getCnHeader() { + return cnHeader; + } - @Value("${hawkbit.server.controller.security.rp.trustedIPs:#{null}}") - private List rpTrustedIPs; + /** + * @param cnHeader + * the cnHeader to set + */ + public void setCnHeader(final String cnHeader) { + this.cnHeader = cnHeader; + } - @Value("${hawkbit.server.controller.security.authentication.anonymous.enabled:false}") - private Boolean anonymousEnabled; + /** + * @return the sslIssuerHashHeader + */ + public String getSslIssuerHashHeader() { + return sslIssuerHashHeader; + } + + /** + * @param sslIssuerHashHeader + * the sslIssuerHashHeader to set + */ + public void setSslIssuerHashHeader(final String sslIssuerHashHeader) { + this.sslIssuerHashHeader = sslIssuerHashHeader; + } + + /** + * @return the trustedIPs + */ + public List getTrustedIPs() { + return trustedIPs; + } + + /** + * @param trustedIPs + * the trustedIPs to set + */ + public void setTrustedIPs(final List trustedIPs) { + this.trustedIPs = trustedIPs; + } + + } + + /** + * Inner class for anonymous enable configuration. + */ + @Component + @ConfigurationProperties("hawkbit.server.controller.security.authentication.anonymous") + public static class AnoymousAuthenticationProperties { + private Boolean enabled = Boolean.FALSE; + + /** + * @param enabled + * the enabled to set + */ + public void setEnabled(final Boolean enabled) { + this.enabled = enabled; + } + + /** + * @return the enabled + */ + public Boolean getEnabled() { + return enabled; + } + + } + + @Autowired + private RpProperties rppProperties; + + @Autowired + private AnoymousAuthenticationProperties authenticationsProperties; public String getRpCnHeader() { - return rpCnHeader; + return rppProperties.getCnHeader(); } public String getRpSslIssuerHashHeader() { - return rpSslIssuerHashHeader; + return rppProperties.getSslIssuerHashHeader(); } public List getRpTrustedIPs() { - return rpTrustedIPs; + return rppProperties.getTrustedIPs(); } public Boolean getAnonymousEnabled() { - return anonymousEnabled; + return authenticationsProperties.getEnabled(); } - public void setRpCnHeader(final String rpCnHeader) { - this.rpCnHeader = rpCnHeader; - } - - public void setRpSslIssuerHashHeader(final String rpSslIssuerHashHeader) { - this.rpSslIssuerHashHeader = rpSslIssuerHashHeader; - } - - public void setRpTrustedIPs(final List rpTrustedIPs) { - this.rpTrustedIPs = rpTrustedIPs; - } - - public void setAnonymousEnabled(final Boolean anonymousEnabled) { - this.anonymousEnabled = anonymousEnabled; - } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java index fa4022a3e..0716e6bd6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java @@ -207,39 +207,12 @@ public class BulkUploadHandler extends CustomComponent @Override public void run() { - long innerCounter = 0; - String line; if (tempFile == null) { return; } try (InputStream tempStream = new FileInputStream(tempFile)) { - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(tempStream, Charset.defaultCharset()))) { - LOG.info("Bulk file upload started"); - final double totalFileSize = getTotalNumberOfLines(); - - /** - * Once control is in upload succeeded method automatically - * upload button is re-enabled. To disable the button firing - * below event. - */ - eventBus.publish(this, new TargetTableEvent(TargetComponentEvent.BULK_UPLOAD_PROCESS_STARTED)); - while ((line = reader.readLine()) != null) { - innerCounter++; - readEachLine(line, innerCounter, totalFileSize); - } - doAssignments(); - eventBus.publish(this, new TargetTableEvent(TargetComponentEvent.BULK_UPLOAD_COMPLETED)); - - // Clearing after assignments are done - managementUIState.getTargetTableFilters().getBulkUpload().getTargetsCreated().clear(); - } catch (final IOException e) { - LOG.error("Error reading file {}", tempFile.getName(), e); - } finally { - resetCounts(); - deleteFile(); - } + readFileStream(tempStream); } catch (final FileNotFoundException e) { LOG.error("Temporary file not found with name {}", tempFile.getName(), e); } catch (final IOException e) { @@ -248,6 +221,37 @@ public class BulkUploadHandler extends CustomComponent } + private void readFileStream(final InputStream tempStream) { + String line; + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(tempStream, Charset.defaultCharset()))) { + LOG.info("Bulk file upload started"); + long innerCounter = 0; + final double totalFileSize = getTotalNumberOfLines(); + + /** + * Once control is in upload succeeded method automatically + * upload button is re-enabled. To disable the button firing + * below event. + */ + eventBus.publish(this, new TargetTableEvent(TargetComponentEvent.BULK_UPLOAD_PROCESS_STARTED)); + while ((line = reader.readLine()) != null) { + innerCounter++; + readEachLine(line, innerCounter, totalFileSize); + } + doAssignments(); + eventBus.publish(this, new TargetTableEvent(TargetComponentEvent.BULK_UPLOAD_COMPLETED)); + + // Clearing after assignments are done + managementUIState.getTargetTableFilters().getBulkUpload().getTargetsCreated().clear(); + } catch (final IOException e) { + LOG.error("Error reading file {}", tempFile.getName(), e); + } finally { + resetCounts(); + deleteFile(); + } + } + private void doAssignments() { final StringBuilder errorMessage = new StringBuilder(); String dsAssignmentFailedMsg = null;