Merge remote-tracking branch 'eclipse/master'
This commit is contained in:
@@ -13,7 +13,7 @@ run org.eclipse.hawkbit.simulator.DeviceSimulator
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
The simulator has user authentication enabled by default. Default credentials:
|
The simulator has user authentication enabled in **cloud profile**. Default credentials:
|
||||||
* username : admin
|
* username : admin
|
||||||
* passwd : admin
|
* passwd : admin
|
||||||
|
|
||||||
@@ -29,8 +29,6 @@ The UI can be accessed via the URL:
|
|||||||
http://localhost:8083
|
http://localhost:8083
|
||||||
```
|
```
|
||||||
|
|
||||||
`Basic Authentication Credentials are admin / admin`
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
@@ -45,6 +43,10 @@ Optional parameters:
|
|||||||
* name : name prefix simulated devices (default: "dmfSimulated"), followed by counter
|
* name : name prefix simulated devices (default: "dmfSimulated"), followed by counter
|
||||||
* amount : number of simulated devices (default: 20, capped at: 4000)
|
* amount : number of simulated devices (default: 20, capped at: 4000)
|
||||||
* tenant : in a multi-tenenat ready hawkBit installation (default: "DEFAULT")
|
* 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)
|
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
|
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
|
||||||
|
```
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator;
|
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.AbstractSimulatedDevice.Protocol;
|
||||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@@ -42,19 +46,49 @@ public class SimulationController {
|
|||||||
* the amount of devices to be created
|
* the amount of devices to be created
|
||||||
* @param tenant
|
* @param tenant
|
||||||
* the tenant to create the device to
|
* 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
|
* @return a response string that devices has been created
|
||||||
|
* @throws MalformedURLException
|
||||||
*/
|
*/
|
||||||
@RequestMapping("/start")
|
@RequestMapping("/start")
|
||||||
String start(@RequestParam(value = "name", defaultValue = "dmfSimulated") final String name,
|
ResponseEntity<String> start(@RequestParam(value = "name", defaultValue = "simulated") final String name,
|
||||||
@RequestParam(value = "amount", defaultValue = "20") final int amount,
|
@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++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
final String deviceId = name + 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);
|
spSenderService.createOrUpdateThing(tenant, deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Updated " + amount + " DMF connected targets!";
|
return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ public class GenerateDialog extends Window {
|
|||||||
tf5.setIcon(FontAwesome.FLAG_O);
|
tf5.setIcon(FontAwesome.FLAG_O);
|
||||||
tf5.setRequired(true);
|
tf5.setRequired(true);
|
||||||
tf5.setVisible(false);
|
tf5.setVisible(false);
|
||||||
tf5.addValidator(new RegexpValidator(
|
tf5.addValidator(new RegexpValidator("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]",
|
||||||
"^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "is not an URL"));
|
"is not an URL"));
|
||||||
|
|
||||||
final TextField tf6 = new TextField("gateway token", "");
|
final TextField tf6 = new TextField("gateway token", "");
|
||||||
tf6.setColumns(50);
|
tf6.setColumns(50);
|
||||||
@@ -125,7 +125,8 @@ public class GenerateDialog extends Window {
|
|||||||
@Override
|
@Override
|
||||||
public void buttonClick(final ClickEvent event) {
|
public void buttonClick(final ClickEvent event) {
|
||||||
try {
|
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(),
|
Integer.valueOf(tf4.getValue().replace(".", "")), new URL(tf5.getValue()), tf6.getValue(),
|
||||||
(Protocol) protocolGroup.getValue());
|
(Protocol) protocolGroup.getValue());
|
||||||
} catch (final NumberFormatException e) {
|
} catch (final NumberFormatException e) {
|
||||||
|
|||||||
@@ -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/**
|
||||||
@@ -27,23 +27,5 @@ spring.rabbitmq.port=5672
|
|||||||
spring.rabbitmq.dynamic=true
|
spring.rabbitmq.dynamic=true
|
||||||
spring.rabbitmq.listener.prefetch=100
|
spring.rabbitmq.listener.prefetch=100
|
||||||
|
|
||||||
# SECURITY (SecurityProperties)
|
security.basic.enabled=false
|
||||||
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/**
|
|
||||||
|
|
||||||
server.port=8083
|
server.port=8083
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ package org.eclipse.hawkbit.security;
|
|||||||
|
|
||||||
import java.util.List;
|
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.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The common properties for security.
|
* The common properties for security.
|
||||||
@@ -22,47 +23,108 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
@ConfigurationProperties
|
@ConfigurationProperties
|
||||||
public class SecurityProperties {
|
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<String> 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<String> 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<String> getTrustedIPs() {
|
||||||
|
return trustedIPs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param trustedIPs
|
||||||
|
* the trustedIPs to set
|
||||||
|
*/
|
||||||
|
public void setTrustedIPs(final List<String> 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() {
|
public String getRpCnHeader() {
|
||||||
return rpCnHeader;
|
return rppProperties.getCnHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRpSslIssuerHashHeader() {
|
public String getRpSslIssuerHashHeader() {
|
||||||
return rpSslIssuerHashHeader;
|
return rppProperties.getSslIssuerHashHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getRpTrustedIPs() {
|
public List<String> getRpTrustedIPs() {
|
||||||
return rpTrustedIPs;
|
return rppProperties.getTrustedIPs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getAnonymousEnabled() {
|
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<String> rpTrustedIPs) {
|
|
||||||
this.rpTrustedIPs = rpTrustedIPs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAnonymousEnabled(final Boolean anonymousEnabled) {
|
|
||||||
this.anonymousEnabled = anonymousEnabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,39 +207,12 @@ public class BulkUploadHandler extends CustomComponent
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long innerCounter = 0;
|
|
||||||
String line;
|
|
||||||
if (tempFile == null) {
|
if (tempFile == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream tempStream = new FileInputStream(tempFile)) {
|
try (InputStream tempStream = new FileInputStream(tempFile)) {
|
||||||
try (BufferedReader reader = new BufferedReader(
|
readFileStream(tempStream);
|
||||||
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();
|
|
||||||
}
|
|
||||||
} catch (final FileNotFoundException e) {
|
} catch (final FileNotFoundException e) {
|
||||||
LOG.error("Temporary file not found with name {}", tempFile.getName(), e);
|
LOG.error("Temporary file not found with name {}", tempFile.getName(), e);
|
||||||
} catch (final IOException 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() {
|
private void doAssignments() {
|
||||||
final StringBuilder errorMessage = new StringBuilder();
|
final StringBuilder errorMessage = new StringBuilder();
|
||||||
String dsAssignmentFailedMsg = null;
|
String dsAssignmentFailedMsg = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user