Merge remote-tracking branch 'eclipse/master' into harmonize-test-documentation

This commit is contained in:
Kai Zimmermann
2016-03-04 18:14:26 +01:00
61 changed files with 1216 additions and 652 deletions

6
.gitignore vendored
View File

@@ -16,13 +16,13 @@
*.jar *.jar
*.war *.war
######################
# Sonar # Sonar
######################
.sonar_lock .sonar_lock
# Eclipse IDE # Created by spring-boot-configuration-processor
.factorypath
# Eclipse IDE
*.pydevproject *.pydevproject
.project .project
.metadata .metadata

9
MIGRATION.md Normal file
View File

@@ -0,0 +1,9 @@
# hawkBit Migration Guides
## Release 0.2
### Configuration Property changes
- hawkbit.server.controller._ have changed to hawkbit.server.ddi._
- info.build._ have changed to hawkbit.server.build._
- hawkbit.server.demo._ have changed to hawkbit.server.ui.demo._
- hawkbit.server.email.support has changed to hawkbit.server.ui.links.support
- hawkbit.server.email.request.account has changed to hawkbit.server.ui.links.requestAccount
- hawkbit.server.im.login.url has changed to hawkbit.server.ui.links.userManagement

View File

@@ -100,7 +100,6 @@
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>19.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.netflix.feign</groupId> <groupId>com.netflix.feign</groupId>
@@ -116,6 +115,11 @@
<groupId>com.jayway.jsonpath</groupId> <groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId> <artifactId>json-path</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@@ -9,21 +9,35 @@
package org.eclipse.hawkbit.simulator.amqp; package org.eclipse.hawkbit.simulator.amqp;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** /**
* Bean which holds the necessary properties for configuring the AMQP * Bean which holds the necessary properties for configuring the AMQP
* connection. * connection.
* *
*
*
*/ */
@Component
@ConfigurationProperties("hawkbit.device.simulator.amqp") @ConfigurationProperties("hawkbit.device.simulator.amqp")
public class AmqpProperties { public class AmqpProperties {
/**
* Queue for receiving DMF messages from update server.
*/
private String receiverConnectorQueueFromSp; private String receiverConnectorQueueFromSp;
/**
* Exchange for sending DMF messages to update server.
*/
private String senderForSpExchange; private String senderForSpExchange;
/**
* Simulator dead letter queue.
*/
private String deadLetterQueue; private String deadLetterQueue;
/**
* Simulator dead letter exchange.
*/
private String deadLetterExchange; private String deadLetterExchange;
public String getReceiverConnectorQueueFromSp() { public String getReceiverConnectorQueueFromSp() {

View File

@@ -7,23 +7,20 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# need to re-name these properties in the defaulthawkbit.properties and code! hawkbit.server.ddi.security.authentication.anonymous.enabled=true
hawkbit.server.controller.security.authentication.anonymous.enabled=true hawkbit.server.ddi.security.authentication.targettoken.enabled=false
hawkbit.server.controller.security.authentication.header.enabled=false hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
hawkbit.server.controller.security.authentication.targettoken.enabled=false
hawkbit.server.controller.security.authentication.gatewaytoken.enabled=false
spring.profiles.active=amqp spring.profiles.active=amqp
vaadin.servlet.productionMode=false vaadin.servlet.productionMode=false
vaadin.static.servlet.productionMode=false
## Configuration for RabbitMQ integration ## Configuration for RabbitMQ integration
hawkbit.server.amqp.username=guest spring.rabbitmq.username=guest
hawkbit.server.amqp.password=guest spring.rabbitmq.password=guest
hawkbit.server.amqp.virtualHost=/ spring.rabbitmq.virtualHost=/
hawkbit.server.amqp.host=localhost spring.rabbitmq.host=localhost
hawkbit.server.amqp.port=5672 spring.rabbitmq.port=5672
hawkbit.server.amqp.deadLetterQueue=sp_deadletter hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
hawkbit.server.amqp.deadLetterExchange=sp.deadletter hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
hawkbit.server.amqp.receiverQueue=sp_receiver hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver

View File

@@ -87,5 +87,10 @@
<artifactId>google-collections</artifactId> <artifactId>google-collections</artifactId>
<version>1.0-rc2</version> <version>1.0-rc2</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -9,17 +9,30 @@
package org.eclipse.hawkbit.mgmt.client; package org.eclipse.hawkbit.mgmt.client;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** /**
* Configuration bean which holds the configuration of the client e.g. the base * Configuration bean which holds the configuration of the client e.g. the base
* URL of the hawkbit-server and the credentials to use the RESTful Management * URL of the hawkbit-server and the credentials to use the RESTful Management
* API. * API.
*/ */
@Component
@ConfigurationProperties(prefix = "hawkbit") @ConfigurationProperties(prefix = "hawkbit")
public class ClientConfigurationProperties { public class ClientConfigurationProperties {
/**
* Update server URI.
*/
private String url = "localhost:8080"; private String url = "localhost:8080";
/**
* Update server user name.
*/
private String username = "admin"; private String username = "admin";
/**
* Update server password.
*/
private String password = "admin"; // NOSONAR this password is only used for private String password = "admin"; // NOSONAR this password is only used for
// examples // examples

View File

@@ -11,4 +11,4 @@ hawkbit.url=localhost:8080
hawkbit.username=admin hawkbit.username=admin
hawkbit.password=admin hawkbit.password=admin
spring.main.banner-mode=OFF spring.main.show-banner=false

View File

@@ -72,5 +72,10 @@
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId> <artifactId>spring-context-support</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,27 +0,0 @@
/**
* 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
*/
package org.eclipse.hawkbit.autoconfigure.conf;
import org.eclipse.hawkbit.ControllerPollProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Enable the Controlle Poll.
*
*
*
*/
@Configuration
@ConditionalOnClass(ControllerPollProperties.class)
@EnableConfigurationProperties(ControllerPollProperties.class)
public class ControllerPollAutoConfiguration {
}

View File

@@ -13,17 +13,29 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* Properties for the async configurer. * Properties for the async configurer.
* *
*
*/ */
@ConfigurationProperties("hawkbit.threadpool") @ConfigurationProperties("hawkbit.threadpool")
public class AsyncConfigurerThreadpoolProperties { public class AsyncConfigurerThreadpoolProperties {
/**
* Max queue size for central event executor.
*/
private Integer queuesize = 250; private Integer queuesize = 250;
/**
* Core processing threads for central event executor.
*/
private Integer corethreads = 5; private Integer corethreads = 5;
/**
* Maximum thread pool size for central event executor.
*/
private Integer maxthreads = 50; private Integer maxthreads = 50;
/**
* When the number of threads is greater than the core, this is the maximum
* time that excess idle threads will wait for new tasks before terminating.
*/
private Long idletimeout = 10000L; private Long idletimeout = 10000L;
public Integer getQueuesize() { public Integer getQueuesize() {

View File

@@ -26,10 +26,9 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
/** /**
* * Central event processors inside update server.
* *
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(AsyncConfigurerThreadpoolProperties.class) @EnableConfigurationProperties(AsyncConfigurerThreadpoolProperties.class)
public class ExecutorAutoConfiguration { public class ExecutorAutoConfiguration {
@@ -40,7 +39,7 @@ public class ExecutorAutoConfiguration {
private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties; private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties;
/** /**
* @return ExecutorService for general pupose multi threaded operations * @return ExecutorService for general purpose multi threaded operations
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean

View File

@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter; import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityProperties; import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -53,7 +53,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
* *
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(DdiSecurityProperties.class)
public class SecurityAutoConfiguration { public class SecurityAutoConfiguration {
/** /**

View File

@@ -31,29 +31,27 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.rest.resource.RestConstants; import org.eclipse.hawkbit.rest.resource.RestConstants;
import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSource; import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSource;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DosFilter; import org.eclipse.hawkbit.security.DosFilter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.HttpDownloadAuthenticationFilter; import org.eclipse.hawkbit.security.HttpDownloadAuthenticationFilter;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -94,17 +92,11 @@ import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSucc
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true)
@EnableWebMvcSecurity @EnableWebMvcSecurity
@Order(value = Ordered.HIGHEST_PRECEDENCE) @Order(value = Ordered.HIGHEST_PRECEDENCE)
public class SecurityManagedConfiguration implements EnvironmentAware { public class SecurityManagedConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class); private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class);
private static final String SP_SERVER_CONFIG_PREFIX = "hawkbit.server."; @Autowired
private RelaxedPropertyResolver environment; private HawkbitSecurityProperties securityProperties;
@Override
public void setEnvironment(final Environment environment) {
this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX);
}
/** /**
* {@link WebSecurityConfigurer} for the internal SP controller API. * {@link WebSecurityConfigurer} for the internal SP controller API.
@@ -123,7 +115,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired @Autowired
private SecurityProperties securityConfiguration; private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@@ -132,7 +124,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource(); final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter( final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
securityConfiguration.getRpCnHeader(), securityConfiguration.getRpSslIssuerHashHeader(), ddiSecurityConfiguration.getRp().getCnHeader(), ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(),
systemManagement, tenantAware); systemManagement, tenantAware);
securityHeaderFilter.setAuthenticationManager(authenticationManager()); securityHeaderFilter.setAuthenticationManager(authenticationManager());
securityHeaderFilter.setCheckForPrincipalChanges(true); securityHeaderFilter.setCheckForPrincipalChanges(true);
@@ -158,7 +150,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} }
if (securityConfiguration.getAnonymousEnabled()) { if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info( LOG.info(
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************"); "******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter( final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
@@ -189,7 +181,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Override @Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception { protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider( auth.authenticationProvider(
new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs())); new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }
@@ -204,13 +196,10 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
public FilterRegistrationBean dosFilter() { public FilterRegistrationBean dosFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(),
.setFilter( securityProperties.getDos().getFilter().getMaxWrite(),
new DosFilter(environment.getProperty("security.dos.filter.maxRead", Integer.class, 200), securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(),
environment.getProperty("security.dos.filter.maxWrite", Integer.class, 50), securityProperties.getClients().getRemoteIpHeader()));
environment.getProperty("security.dos.filter.whitelist"), environment
.getProperty("security.clients.blacklist"),
environment.getProperty("security.rp.remote_ip_header", String.class, "X-Forwarded-For")));
filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*"); filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*");
return filterRegBean; return filterRegBean;
} }
@@ -310,8 +299,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Configuration @Configuration
@Order(400) @Order(400)
@EnableVaadinSecurity @EnableVaadinSecurity
public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
implements EnvironmentAware {
private static final String XFRAME_OPTION_DENY = "DENY"; private static final String XFRAME_OPTION_DENY = "DENY";
private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN"; private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN";
@@ -320,13 +308,8 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
private VaadinSecurityContext vaadinSecurityContext; private VaadinSecurityContext vaadinSecurityContext;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Autowired
private RelaxedPropertyResolver environment; private HawkbitSecurityProperties securityProperties;
@Override
public void setEnvironment(final Environment environment) {
this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX);
}
/** /**
* post construct for setting the authentication success handler for the * post construct for setting the authentication success handler for the
@@ -379,13 +362,13 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
// configuration xframe-option // configuration xframe-option
final String confXframeOption = environment.getProperty("security.xframe.option", XFRAME_OPTION_DENY); final String confXframeOption = securityProperties.getXframe().getOption();
final String confAllowFromUri = environment.getProperty("security.xframe.option.allowfrom"); final String confAllowFromUri = securityProperties.getXframe().getAllowfrom();
if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri == null) { if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri.isEmpty()) {
// if allow-from option is specified but no allowFromUri throw // if allow-from option is specified but no allowFromUri throw
// exception // exception
throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM" throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM"
+ " but no hawkbit.server.security.xframe.option.allowfrom has been set, " + " but no hawkbit.server.security.xframe.allowfrom has been set, "
+ "please ensure to set allow from URIs"); + "please ensure to set allow from URIs");
} }
@@ -461,7 +444,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired @Autowired
private SecurityProperties securityConfiguration; private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired @Autowired
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) @Qualifier(CacheConstants.DOWNLOAD_ID_CACHE)
@@ -484,7 +467,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Override @Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception { protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider( auth.authenticationProvider(
new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs())); new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.autoconfigure.url;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import org.eclipse.hawkbit.HawkbitServerProperties;
import org.eclipse.hawkbit.api.HostnameResolver; import org.eclipse.hawkbit.api.HostnameResolver;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -27,11 +28,11 @@ import com.google.common.base.Throwables;
* *
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(ServerProperties.class) @EnableConfigurationProperties(HawkbitServerProperties.class)
public class PropertyHostnameResolverAutoConfiguration { public class PropertyHostnameResolverAutoConfiguration {
@Autowired @Autowired
private ServerProperties serverProperties; private HawkbitServerProperties serverProperties;
/** /**
* @return the default autoconfigure hostname resolver implementation which * @return the default autoconfigure hostname resolver implementation which

View File

@@ -1,30 +0,0 @@
/**
* 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
*/
package org.eclipse.hawkbit.autoconfigure.url;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for the server e.g. the server's URL which must be configured.
*
*
*/
@ConfigurationProperties("hawkbit.server")
public class ServerProperties {
private String url = "http://localhost:8080";
public String getUrl() {
return url;
}
public void setUrl(final String url) {
this.url = url;
}
}

View File

@@ -11,5 +11,4 @@ org.eclipse.hawkbit.autoconfigure.eventbus.EventBusAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.RedisAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.cache.RedisAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.conf.ControllerPollAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration

View File

@@ -7,12 +7,6 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# Tomcat / Server
server.tomcat.compression=on
spring.http.gzip.mime-types=text/html,text/xml,text/plain,application/json,application/javascript,text/css,application/x-javascript,text/javascript,application/vnd.ms-fontobject,application/x-font-opentype,application/x-font-truetype,application/x-font-ttf,application/xml,font/eot,font/opentype,font/otf,image/svg+xml,image/vnd.microsoft.icon
server.tomcat.compressable-mime-types=${spring.http.gzip.mime-types}
spring.http.gzip.min-gzip-size=256
# JPA / Datasource # JPA / Datasource
spring.jpa.eclipselink.eclipselink.weaving=false spring.jpa.eclipselink.eclipselink.weaving=false
spring.jpa.database=H2 spring.jpa.database=H2
@@ -21,7 +15,6 @@ spring.datasource.driverClassName=org.h2.Driver
# MongoDB for artifact-repository # MongoDB for artifact-repository
spring.data.mongodb.uri=mongodb://localhost/artifactrepo spring.data.mongodb.uri=mongodb://localhost/artifactrepo
spring.data.mongo.repositories.enabled=true
# Flyway DDL # Flyway DDL
flyway.enabled=true flyway.enabled=true
@@ -29,15 +22,10 @@ flyway.initOnMigrate=true
flyway.sqlMigrationSuffix=${spring.jpa.database}.sql flyway.sqlMigrationSuffix=${spring.jpa.database}.sql
# Vaadin Servlet # Vaadin Servlet
vaadin.static.servlet.productionMode=true
vaadin.servlet.productionMode=true vaadin.servlet.productionMode=true
vaadin.servlet.urlMapping=/UI/* vaadin.servlet.urlMapping=/UI/*
vaadin.servlet.params.heartbeatInterval=60 vaadin.servlet.heartbeatInterval=60
vaadin.servlet.params.closeIdleSessions=false vaadin.servlet.closeIdleSessions=false
# Spring MVC
spring.mvc.favicon.enabled=false
# Defines the thread pool executor # Defines the thread pool executor
hawkbit.threadpool.corethreads=5 hawkbit.threadpool.corethreads=5
@@ -50,6 +38,6 @@ hawkbit.controller.pollingTime=00:05:00
hawkbit.controller.pollingOverdueTime=00:05:00 hawkbit.controller.pollingOverdueTime=00:05:00
## Configuration for RabbitMQ integration ## Configuration for RabbitMQ integration
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_receiver_deadletter
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.receiver.deadletter
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver

View File

@@ -37,6 +37,11 @@
<groupId>redis.clients</groupId> <groupId>redis.clients</groupId>
<artifactId>jedis</artifactId> <artifactId>jedis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test --> <!-- Test -->

View File

@@ -26,8 +26,6 @@ import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
* The spring Redis configuration which is enabled by using the profile * The spring Redis configuration which is enabled by using the profile
* {@code redis} to use a Redis server as cache. * {@code redis} to use a Redis server as cache.
* *
*
*
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(RedisProperties.class) @EnableConfigurationProperties(RedisProperties.class)

View File

@@ -14,14 +14,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Bean which holds the necessary properties for configuring the Redis * Bean which holds the necessary properties for configuring the Redis
* connection. * connection.
* *
*
*
*
*/ */
@ConfigurationProperties("hawkbit.server.redis") @ConfigurationProperties("hawkbit.server.redis")
public class RedisProperties { public class RedisProperties {
/**
* Redis server hostname.
*/
private String host; private String host;
/**
* Redis server port.
*/
private int port; private int port;
/** /**

View File

@@ -43,6 +43,11 @@
<artifactId>allure-junit-adaptor</artifactId> <artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -9,18 +9,26 @@
package org.eclipse.hawkbit; package org.eclipse.hawkbit;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** /**
* Defines the polling time for the controllers in HH:MM:SS notation. * Defines the polling time for the controllers in HH:MM:SS notation.
* *
*
*
*/ */
@Component
@ConfigurationProperties(prefix = "hawkbit.controller") @ConfigurationProperties(prefix = "hawkbit.controller")
public class ControllerPollProperties { public class ControllerPollProperties {
/**
* Recommended target polling time for DDI API. Final choice is up to the
* target.
*/
private String pollingTime = "00:05:00"; private String pollingTime = "00:05:00";
/**
* Assumed time frame where the target is considered overdue when no DDI
* polling has been registered by the update server.
*/
private String pollingOverdueTime = "00:05:00"; private String pollingOverdueTime = "00:05:00";
public String getPollingTime() { public String getPollingTime() {

View File

@@ -0,0 +1,97 @@
/**
* 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
*/
package org.eclipse.hawkbit;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for the server e.g. the server's URL which must be configured.
*
*/
@ConfigurationProperties("hawkbit.server")
public class HawkbitServerProperties {
/**
* Defines under which URI the update server can be reached. Used to
* calculate download URLs for DMF transmitted update actions.
*/
private String url = "http://localhost:8080";
private final Build build = new Build();
public Build getBuild() {
return build;
}
/**
* Build information of the hawkBit instance. Influenced by maven.
*
*/
public static class Build {
/**
* Project artifact ID.
*/
private String artifact = "";
/**
* Project name.
*/
private String name = "";
/**
* Project description.
*/
private String description = "";
/**
* Project version.
*/
private String version = "";
public String getArtifact() {
return artifact;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getVersion() {
return version;
}
public void setArtifact(final String artifact) {
this.artifact = artifact;
}
public void setName(final String name) {
this.name = name;
}
public void setDescription(final String description) {
this.description = description;
}
public void setVersion(final String version) {
this.version = version;
}
}
public String getUrl() {
return url;
}
public void setUrl(final String url) {
this.url = url;
}
}

View File

@@ -20,7 +20,12 @@ public enum ActionStatusFields implements FieldNameProvider {
/** /**
* The id field. * The id field.
*/ */
ID("id"); ID("id"),
/**
* The reportedAt field.
*/
REPORTEDAT("createdAt");
private final String fieldName; private final String fieldName;

View File

@@ -22,35 +22,35 @@ public enum TenantConfigurationKey {
* boolean value {@code true} {@code false}. * boolean value {@code true} {@code false}.
*/ */
AUTHENTICATION_MODE_HEADER_ENABLED("authentication.header.enabled", AUTHENTICATION_MODE_HEADER_ENABLED("authentication.header.enabled",
"hawkbit.server.controller.security.authentication.header.enabled", Boolean.FALSE.toString()), "hawkbit.server.ddi.security.authentication.header.enabled", Boolean.FALSE.toString()),
/** /**
* *
*/ */
AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME("authentication.header.authority", AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME("authentication.header.authority",
"hawkbit.server.controller.security.authentication.header.authority", Boolean.FALSE.toString()), "hawkbit.server.ddi.security.authentication.header.authority", Boolean.FALSE.toString()),
/** /**
* boolean value {@code true} {@code false}. * boolean value {@code true} {@code false}.
*/ */
AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED("authentication.targettoken.enabled", AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED("authentication.targettoken.enabled",
"hawkbit.server.controller.security.authentication.targettoken.enabled", Boolean.FALSE.toString()), "hawkbit.server.ddi.security.authentication.targettoken.enabled", Boolean.FALSE.toString()),
/** /**
* boolean value {@code true} {@code false}. * boolean value {@code true} {@code false}.
*/ */
AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED("authentication.gatewaytoken.enabled", AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED("authentication.gatewaytoken.enabled",
"hawkbit.server.controller.security.authentication.gatewaytoken.enabled", Boolean.FALSE.toString()), "hawkbit.server.ddi.security.authentication.gatewaytoken.enabled", Boolean.FALSE.toString()),
/** /**
* string value which holds the name of the security token key. * string value which holds the name of the security token key.
*/ */
AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME("authentication.gatewaytoken.name", AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME("authentication.gatewaytoken.name",
"hawkbit.server.controller.security.authentication.gatewaytoken.name", null), "hawkbit.server.ddi.security.authentication.gatewaytoken.name", null),
/** /**
* string value which holds the actual security-key of the gateway security * string value which holds the actual security-key of the gateway security
* token. * token.
*/ */
AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY("authentication.gatewaytoken.key", AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY("authentication.gatewaytoken.key",
"hawkbit.server.controller.security.authentication.gatewaytoken.key", null); "hawkbit.server.ddi.security.authentication.gatewaytoken.key", null);
private final String keyName; private final String keyName;
private final String defaultKeyName; private final String defaultKeyName;

View File

@@ -60,6 +60,11 @@
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test --> <!-- Test -->

View File

@@ -21,9 +21,9 @@ import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.PreAuthenficationFilter; import org.eclipse.hawkbit.security.PreAuthenficationFilter;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -55,7 +55,7 @@ public class AmqpControllerAuthentfication {
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired @Autowired
private SecurityProperties secruityProperties; private DdiSecurityProperties ddiSecruityProperties;
/** /**
* Constructor. * Constructor.
@@ -78,8 +78,8 @@ public class AmqpControllerAuthentfication {
filterChain.add(gatewaySecurityTokenFilter); filterChain.add(gatewaySecurityTokenFilter);
final ControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new ControllerPreAuthenticatedSecurityHeaderFilter( final ControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new ControllerPreAuthenticatedSecurityHeaderFilter(
secruityProperties.getRpCnHeader(), secruityProperties.getRpSslIssuerHashHeader(), systemManagement, ddiSecruityProperties.getRp().getCnHeader(), ddiSecruityProperties.getRp().getSslIssuerHashHeader(),
tenantAware); systemManagement, tenantAware);
filterChain.add(securityHeaderFilter); filterChain.add(securityHeaderFilter);
final ControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new ControllerPreAuthenticateSecurityTokenFilter( final ControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new ControllerPreAuthenticateSecurityTokenFilter(
@@ -134,8 +134,8 @@ public class AmqpControllerAuthentfication {
this.controllerManagement = controllerManagement; this.controllerManagement = controllerManagement;
} }
public void setSecruityProperties(final SecurityProperties secruityProperties) { public void setSecruityProperties(final DdiSecurityProperties secruityProperties) {
this.secruityProperties = secruityProperties; this.ddiSecruityProperties = secruityProperties;
} }
public void setSystemManagement(final SystemManagement systemManagement) { public void setSystemManagement(final SystemManagement systemManagement) {

View File

@@ -8,41 +8,91 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** /**
* Bean which holds the necessary properties for configuring the AMQP * Bean which holds the necessary properties for configuring the AMQP
* connection. * connection.
* *
*/ */
@Component
@ConfigurationProperties("hawkbit.dmf.rabbitmq") @ConfigurationProperties("hawkbit.dmf.rabbitmq")
public class AmqpProperties { public class AmqpProperties {
/**
* DMF API dead letter queue.
*/
private String deadLetterQueue = "dmf_receiver_deadletter"; private String deadLetterQueue = "dmf_receiver_deadletter";
/**
* DMF API dead letter exchange.
*/
private String deadLetterExchange = "dmf.receiver.deadletter"; private String deadLetterExchange = "dmf.receiver.deadletter";
/**
* DMF API receiving queue.
*/
private String receiverQueue = "dmf_receiver"; private String receiverQueue = "dmf_receiver";
/**
* Missing queue fatal.
*/
private boolean missingQueuesFatal = false; private boolean missingQueuesFatal = false;
/**
* Is missingQueuesFatal enabled
*
* @see SimpleMessageListenerContainer#setMissingQueuesFatal
* @return the missingQueuesFatal <true> enabled <false> disabled
*/
public boolean isMissingQueuesFatal() { public boolean isMissingQueuesFatal() {
return missingQueuesFatal; return missingQueuesFatal;
} }
/**
* @param missingQueuesFatal
* the missingQueuesFatal to set.
* @see SimpleMessageListenerContainer#setMissingQueuesFatal
*/
public void setMissingQueuesFatal(final boolean missingQueuesFatal) { public void setMissingQueuesFatal(final boolean missingQueuesFatal) {
this.missingQueuesFatal = missingQueuesFatal; this.missingQueuesFatal = missingQueuesFatal;
} }
/**
* Returns the dead letter exchange.
*
* @return dead letter exchange
*/
public String getDeadLetterExchange() { public String getDeadLetterExchange() {
return deadLetterExchange; return deadLetterExchange;
} }
/**
* Sets the dead letter exchange.
*
* @param deadLetterExchange
* the deadLetterExchange to be set
*/
public void setDeadLetterExchange(final String deadLetterExchange) { public void setDeadLetterExchange(final String deadLetterExchange) {
this.deadLetterExchange = deadLetterExchange; this.deadLetterExchange = deadLetterExchange;
} }
/**
* Returns the dead letter queue.
*
* @return the dead letter queue
*/
public String getDeadLetterQueue() { public String getDeadLetterQueue() {
return deadLetterQueue; return deadLetterQueue;
} }
/**
* Sets the dead letter queue.
*
* @param deadLetterQueue
* the deadLetterQueue ro be set
*/
public void setDeadLetterQueue(final String deadLetterQueue) { public void setDeadLetterQueue(final String deadLetterQueue) {
this.deadLetterQueue = deadLetterQueue; this.deadLetterQueue = deadLetterQueue;
} }

View File

@@ -23,8 +23,9 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -67,8 +68,11 @@ public class AmqpControllerAuthenticationTest {
authenticationManager = new AmqpControllerAuthentfication(); authenticationManager = new AmqpControllerAuthentfication();
authenticationManager.setControllerManagement(mock(ControllerManagement.class)); authenticationManager.setControllerManagement(mock(ControllerManagement.class));
final SecurityProperties secruityProperties = mock(SecurityProperties.class);
when(secruityProperties.getRpSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d"); final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class);
final Rp rp = mock(Rp.class);
when(secruityProperties.getRp()).thenReturn(rp);
when(rp.getSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d");
authenticationManager.setSecruityProperties(secruityProperties); authenticationManager.setSecruityProperties(secruityProperties);
systemManagement = mock(SystemManagement.class); systemManagement = mock(SystemManagement.class);
authenticationManager.setSystemManagement(systemManagement); authenticationManager.setSystemManagement(systemManagement);

View File

@@ -56,7 +56,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
} }
@Test @Test
@Description("Tests generate the http download url") @Description("Tests the generation of http download url.")
public void testHttpUrl() { public void testHttpUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTP); final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTP);
assertEquals("http is build incorrect", assertEquals("http is build incorrect",
@@ -67,7 +67,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
} }
@Test @Test
@Description("Tests generate the https download url") @Description("Tests the generation of https download url.")
public void testHttpsUrl() { public void testHttpsUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTPS); final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTPS);
assertEquals("https is build incorrect", assertEquals("https is build incorrect",
@@ -78,7 +78,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
} }
@Test @Test
@Description("Tests generate the coap download url") @Description("Tests the generation of coap download url.")
public void testCoapUrl() { public void testCoapUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.COAP); final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.COAP);

View File

@@ -99,6 +99,11 @@
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test --> <!-- Test -->
<dependency> <dependency>

View File

@@ -40,10 +40,6 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
/** /**
* General configuration for the SP Repository. * General configuration for the SP Repository.
* *
*
*
*
*
*/ */
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository" }) @EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository" })
@EnableTransactionManagement @EnableTransactionManagement

View File

@@ -0,0 +1,50 @@
/**
* 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
*/
package org.eclipse.hawkbit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Rollout Management properties.
*
*/
@Component
@ConfigurationProperties("hawkbit.rollout")
public class RolloutProperties {
private final Scheduler scheduler = new Scheduler();
public Scheduler getScheduler() {
return scheduler;
}
/**
* Rollout scheduler configuration.
*/
public static class Scheduler {
// used by @Scheduled annotation which needs constant
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:30000}";
/**
* Schedule where the rollout scheduler looks necessary state changes in
* milliseconds.
*/
private long fixedDelay = 30000L;
public long getFixedDelay() {
return fixedDelay;
}
public void setFixedDelay(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
}
}

View File

@@ -21,45 +21,47 @@ import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link ActionStatus} repository. * {@link ActionStatus} repository.
* *
*
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface ActionStatusRepository public interface ActionStatusRepository
extends BaseEntityRepository<ActionStatus, Long>, JpaSpecificationExecutor<ActionStatus> { extends BaseEntityRepository<ActionStatus, Long>, JpaSpecificationExecutor<ActionStatus> {
/** /**
* @param target * Counts {@link ActionStatus} entries of given {@link Action} in
* repository.
*
* @param action * @param action
* @return * to count status entries
* @return number of actions in repository
*/ */
Long countByAction(Action action); Long countByAction(Action action);
/** /**
* Counts {@link ActionStatus} entries of given {@link Action} with given
* {@link Status} in repository.
*
* @param action * @param action
* @param retrieved * to count status entries
* @return * @param status
* to filter for
* @return number of actions in repository
*/ */
Long countByActionAndStatus(Action action, Status retrieved); Long countByActionAndStatus(Action action, Status status);
/** /**
* Retrieves all {@link ActionStatus} entries from repository of given
* {@link Action}.
*
* @param pageReq * @param pageReq
* parameters
* @param action * @param action
* @return * of the status entries
* @return pages list of {@link ActionStatus} entries
*/ */
Page<ActionStatus> findByAction(Pageable pageReq, Action action); Page<ActionStatus> findByAction(Pageable pageReq, Action action);
/** /**
* @param pageReq * Finds all status updates for the defined action and target including
* @param action
* @return
*/
Page<ActionStatus> findByActionOrderByIdDesc(Pageable pageReq, Action action);
/**
* Finds all status updates for the defined action and target order by
* {@link ActionStatus#getId()} desc including
* {@link ActionStatus#getMessages()}. * {@link ActionStatus#getMessages()}.
* *
* @param pageReq * @param pageReq
@@ -71,6 +73,6 @@ public interface ActionStatusRepository
* @return Page with found targets * @return Page with found targets
*/ */
@EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD) @EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD)
Page<ActionStatus> getByActionOrderByIdDesc(Pageable pageReq, Action action); Page<ActionStatus> getByAction(Pageable pageReq, Action action);
} }

View File

@@ -33,13 +33,11 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_; import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -57,7 +55,7 @@ import org.springframework.validation.annotation.Validated;
@Transactional(readOnly = true) @Transactional(readOnly = true)
@Validated @Validated
@Service @Service
public class ControllerManagement implements EnvironmentAware { public class ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class); private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
@@ -85,9 +83,8 @@ public class ControllerManagement implements EnvironmentAware {
@Autowired @Autowired
private ActionStatusRepository actionStatusRepository; private ActionStatusRepository actionStatusRepository;
private Integer maxCount = 1000; @Autowired
private HawkbitSecurityProperties securityProperties;
private Integer maxAttributes = 100;
/** /**
* Refreshes the time of the last time the controller has been connected to * Refreshes the time of the last time the controller has been connected to
@@ -379,15 +376,16 @@ public class ControllerManagement implements EnvironmentAware {
} }
private void checkForToManyStatusEntries(final Action action) { private void checkForToManyStatusEntries(final Action action) {
if (maxCount > 0) { if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) {
final Long statusCount = actionStatusRepository.countByAction(action); final Long statusCount = actionStatusRepository.countByAction(action);
if (statusCount >= maxCount) { if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) {
LOG_DOS.error( LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!", "Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
maxCount); securityProperties.getDos().getMaxStatusEntriesPerAction());
throw new ToManyStatusEntriesException(String.valueOf(maxCount)); throw new ToManyStatusEntriesException(
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
} }
} }
} }
@@ -436,10 +434,12 @@ public class ControllerManagement implements EnvironmentAware {
target.getTargetInfo().getControllerAttributes().putAll(data); target.getTargetInfo().getControllerAttributes().putAll(data);
if (target.getTargetInfo().getControllerAttributes().size() > maxAttributes) { if (target.getTargetInfo().getControllerAttributes().size() > securityProperties.getDos()
.getMaxAttributeEntriesPerTarget()) {
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!", LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
maxAttributes); securityProperties.getDos().getMaxAttributeEntriesPerTarget());
throw new ToManyAttributeEntriesException(String.valueOf(maxAttributes)); throw new ToManyAttributeEntriesException(
String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget()));
} }
target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis()); target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis());
@@ -447,19 +447,6 @@ public class ControllerManagement implements EnvironmentAware {
return targetRepository.save(target); return targetRepository.save(target);
} }
/*
* (non-Javadoc)
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@Override
public void setEnvironment(final Environment environment) {
final RelaxedPropertyResolver env = new RelaxedPropertyResolver(environment, "hawkbit.server.");
maxCount = env.getProperty("security.dos.maxStatusEntriesPerAction", Integer.class, 1000);
maxAttributes = env.getProperty("security.dos.maxAttributeEntriesPerTarget", Integer.class, 100);
}
/** /**
* Registers retrieved status for given {@link Target} and {@link Action} if * Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet. * it does not exist yet.

View File

@@ -925,7 +925,7 @@ public class DeploymentManagement {
/** /**
* retrieves all the {@link ActionStatus} entries of the given * retrieves all the {@link ActionStatus} entries of the given
* {@link Action} and {@link Target} in the order latest first. * {@link Action} and {@link Target}.
* *
* @param pageReq * @param pageReq
* pagination parameter * pagination parameter
@@ -937,12 +937,12 @@ public class DeploymentManagement {
* @return the corresponding {@link Page} of {@link ActionStatus} * @return the corresponding {@link Page} of {@link ActionStatus}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<ActionStatus> findActionStatusMessagesByActionInDescOrder(final Pageable pageReq, final Action action, public Page<ActionStatus> findActionStatusByAction(final Pageable pageReq, final Action action,
final boolean withMessages) { final boolean withMessages) {
if (withMessages) { if (withMessages) {
return actionStatusRepository.getByActionOrderByIdDesc(pageReq, action); return actionStatusRepository.getByAction(pageReq, action);
} else { } else {
return actionStatusRepository.findByActionOrderByIdDesc(pageReq, action); return actionStatusRepository.findByAction(pageReq, action);
} }
} }

View File

@@ -46,7 +46,6 @@ import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -55,14 +54,10 @@ import org.springframework.validation.annotation.Validated;
/** /**
* Service layer for generating SP reportings. * Service layer for generating SP reportings.
* *
*
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
@Validated @Validated
@Service @Service
@ConfigurationProperties
public class ReportManagement { public class ReportManagement {
@Value("${spring.jpa.database}") @Value("${spring.jpa.database}")

View File

@@ -10,14 +10,13 @@ package org.eclipse.hawkbit.repository;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.RolloutProperties;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -31,15 +30,10 @@ import org.springframework.stereotype.Component;
// don't active the rollout scheduler in test, otherwise it is hard to test // don't active the rollout scheduler in test, otherwise it is hard to test
// rolloutmanagement and leads weird side-effects maybe. // rolloutmanagement and leads weird side-effects maybe.
@Profile("!test") @Profile("!test")
public class RolloutScheduler implements EnvironmentAware { public class RolloutScheduler {
private static final Logger logger = LoggerFactory.getLogger(RolloutScheduler.class); private static final Logger logger = LoggerFactory.getLogger(RolloutScheduler.class);
private static final String PROP_SCHEDULER_DELAY = "hawkbit.rollout.scheduler.fixedDelay";
private static final long DEFAULT_SCHEDULER_DELAY = 30000L;
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${" + PROP_SCHEDULER_DELAY + ":"
+ DEFAULT_SCHEDULER_DELAY + "}";
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@@ -52,7 +46,8 @@ public class RolloutScheduler implements EnvironmentAware {
@Autowired @Autowired
private SystemSecurityContext systemSecurityContext; private SystemSecurityContext systemSecurityContext;
private long fixedDelay = DEFAULT_SCHEDULER_DELAY; @Autowired
private RolloutProperties rolloutProperties;
/** /**
* Scheduler method called by the spring-async mechanism. Retrieves all * Scheduler method called by the spring-async mechanism. Retrieves all
@@ -60,7 +55,7 @@ public class RolloutScheduler implements EnvironmentAware {
* tenant the {@link RolloutManagement#checkRunningRollouts(long)} in the * tenant the {@link RolloutManagement#checkRunningRollouts(long)} in the
* {@link SystemSecurityContext}. * {@link SystemSecurityContext}.
*/ */
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER) @Scheduled(initialDelayString = RolloutProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void rolloutScheduler() { public void rolloutScheduler() {
logger.debug("rollout schedule checker has been triggered."); logger.debug("rollout schedule checker has been triggered.");
// run this code in system code privileged to have the necessary // run this code in system code privileged to have the necessary
@@ -76,16 +71,11 @@ public class RolloutScheduler implements EnvironmentAware {
logger.info("Checking rollouts for {} tenants", tenants.size()); logger.info("Checking rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) { for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> { tenantAware.runAsTenant(tenant, () -> {
rolloutManagement.checkRunningRollouts(fixedDelay); rolloutManagement.checkRunningRollouts(rolloutProperties.getScheduler().getFixedDelay());
return null; return null;
}); });
} }
return null; return null;
}); });
} }
@Override
public void setEnvironment(final Environment environment) {
fixedDelay = environment.getProperty(PROP_SCHEDULER_DELAY, Long.class, DEFAULT_SCHEDULER_DELAY);
}
} }

View File

@@ -17,8 +17,8 @@ import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.model.helper.EventBusHolder; import org.eclipse.hawkbit.repository.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator; import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator;
import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil; import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
@@ -47,7 +47,7 @@ import com.mongodb.MongoClientOptions;
*/ */
@Configuration @Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true)
@EnableConfigurationProperties({ SecurityProperties.class, ControllerPollProperties.class }) @EnableConfigurationProperties({ DdiSecurityProperties.class, ControllerPollProperties.class })
@Profile("test") @Profile("test")
public class TestConfiguration implements AsyncConfigurer { public class TestConfiguration implements AsyncConfigurer {

View File

@@ -70,8 +70,8 @@ public class ControllerManagementTest extends AbstractIntegrationTest {
.isEqualTo(TargetUpdateStatus.IN_SYNC); .isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3); assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusMessagesByActionInDescOrder(pageReq, savedAction, false) assertThat(deploymentManagement.findActionStatusByAction(pageReq, savedAction, false).getNumberOfElements())
.getNumberOfElements()).isEqualTo(3); .isEqualTo(3);
} }
@Test @Test

View File

@@ -10,7 +10,7 @@
spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value} spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value}
spring.data.mongodb.port=28017 spring.data.mongodb.port=28017
hawkbit.server.controller.security.authentication.header.enabled=true hawkbit.server.ddi.security.authentication.header.enabled=true
hawkbit.server.artifact.repo.upload.maxFileSize=5MB hawkbit.server.artifact.repo.upload.maxFileSize=5MB
@@ -29,11 +29,6 @@ flyway.initOnMigrate=true
flyway.sqlMigrationSuffix=${spring.jpa.database}.sql flyway.sqlMigrationSuffix=${spring.jpa.database}.sql
#spring.jpa.show-sql=true #spring.jpa.show-sql=true
# SP Controller configuration # DDI configuration
hawkbit.controller.pollingTime=00:01:00 hawkbit.controller.pollingTime=00:01:00
hawkbit.controller.pollingOverdueTime=00:01:00 hawkbit.controller.pollingOverdueTime=00:01:00
## Configuration for RabbitMQ integration
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver

View File

@@ -25,13 +25,11 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper; import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.web.bind.annotation.AuthenticationPrincipal; import org.springframework.security.web.bind.annotation.AuthenticationPrincipal;
@@ -55,7 +53,7 @@ import org.springframework.web.bind.annotation.RestController;
*/ */
@RestController @RestController
@RequestMapping(ControllerConstants.ARTIFACTS_V1_REQUEST_MAPPING) @RequestMapping(ControllerConstants.ARTIFACTS_V1_REQUEST_MAPPING)
public class ArtifactStoreController implements EnvironmentAware { public class ArtifactStoreController {
private static final Logger LOG = LoggerFactory.getLogger(ArtifactStoreController.class); private static final Logger LOG = LoggerFactory.getLogger(ArtifactStoreController.class);
@Autowired @Autowired
@@ -67,14 +65,8 @@ public class ArtifactStoreController implements EnvironmentAware {
@Autowired @Autowired
private CacheWriteNotify cacheWriteNotify; private CacheWriteNotify cacheWriteNotify;
private static final String SP_SERVER_CONFIG_PREFIX = "hawkbit.server."; @Autowired
private RelaxedPropertyResolver environment; private HawkbitSecurityProperties securityProperties;
@Override
public void setEnvironment(final Environment environment) {
this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX);
}
/** /**
* Handles GET {@link Artifact} download request. This could be full or * Handles GET {@link Artifact} download request. This could be full or
@@ -138,8 +130,8 @@ public class ArtifactStoreController implements EnvironmentAware {
private Action checkAndReportDownloadByTarget(final HttpServletRequest request, final String targetid, private Action checkAndReportDownloadByTarget(final HttpServletRequest request, final String targetid,
final LocalArtifact artifact) { final LocalArtifact artifact) {
final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil.getClientIpFromRequest( final Target target = controllerManagement.updateLastTargetQuery(targetid,
request, environment.getProperty("security.rp.remote_ip_header", String.class, "X-Forwarded-For"))); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
final Action action = controllerManagement final Action action = controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), artifact.getSoftwareModule()); .getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), artifact.getSoftwareModule());

View File

@@ -41,15 +41,13 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper; import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -74,7 +72,7 @@ import org.springframework.web.bind.annotation.RestController;
*/ */
@RestController @RestController
@RequestMapping(ControllerConstants.BASE_V1_REQUEST_MAPPING) @RequestMapping(ControllerConstants.BASE_V1_REQUEST_MAPPING)
public class RootController implements EnvironmentAware { public class RootController {
private static final Logger LOG = LoggerFactory.getLogger(RootController.class); private static final Logger LOG = LoggerFactory.getLogger(RootController.class);
private static final String GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET = "given action ({}) is not assigned to given target ({})."; private static final String GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET = "given action ({}) is not assigned to given target ({}).";
@@ -99,16 +97,8 @@ public class RootController implements EnvironmentAware {
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
private String requestHeader; @Autowired
private HawkbitSecurityProperties securityProperties;
@Override
public void setEnvironment(final Environment environment) {
final RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(environment,
SP_SERVER_CONFIG_PREFIX);
requestHeader = relaxedPropertyResolver.getProperty("security.rp.remote_ip_header", String.class,
"X-Forwarded-For");
}
/** /**
* Returns all artifacts of a given software module and target. * Returns all artifacts of a given software module and target.
@@ -155,12 +145,13 @@ public class RootController implements EnvironmentAware {
LOG.debug("getControllerBase({})", targetid); LOG.debug("getControllerBase({})", targetid);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid,
IpUtil.getClientIpFromRequest(request, requestHeader)); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) { if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) {
LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid); LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid);
controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED, controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED,
System.currentTimeMillis(), IpUtil.getClientIpFromRequest(request, requestHeader)); System.currentTimeMillis(),
IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
} }
return new ResponseEntity<>( return new ResponseEntity<>(
@@ -195,7 +186,7 @@ public class RootController implements EnvironmentAware {
ResponseEntity<Void> result; ResponseEntity<Void> result;
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, requestHeader)); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
if (checkModule(fileName, module)) { if (checkModule(fileName, module)) {
@@ -265,7 +256,8 @@ public class RootController implements EnvironmentAware {
public ResponseEntity<Void> downloadArtifactMd5(@PathVariable final String targetid, public ResponseEntity<Void> downloadArtifactMd5(@PathVariable final String targetid,
@PathVariable final Long softwareModuleId, @PathVariable final String fileName, @PathVariable final Long softwareModuleId, @PathVariable final String fileName,
final HttpServletResponse response, final HttpServletRequest request) { final HttpServletResponse response, final HttpServletRequest request) {
controllerManagement.updateLastTargetQuery(targetid, IpUtil.getClientIpFromRequest(request, requestHeader)); controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
@@ -311,7 +303,7 @@ public class RootController implements EnvironmentAware {
LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource); LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, requestHeader)); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
final Action action = findActionWithExceptionIfNotFound(actionId); final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) { if (!action.getTarget().getId().equals(target.getId())) {
@@ -362,7 +354,7 @@ public class RootController implements EnvironmentAware {
LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback); LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, requestHeader)); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
if (!actionId.equals(feedback.getId())) { if (!actionId.equals(feedback.getId())) {
LOG.warn( LOG.warn(
@@ -467,7 +459,8 @@ public class RootController implements EnvironmentAware {
+ ControllerConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) + ControllerConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> putConfigData(@Valid @RequestBody final ConfigData configData, public ResponseEntity<Void> putConfigData(@Valid @RequestBody final ConfigData configData,
@PathVariable final String targetid, final HttpServletRequest request) { @PathVariable final String targetid, final HttpServletRequest request) {
controllerManagement.updateLastTargetQuery(targetid, IpUtil.getClientIpFromRequest(request, requestHeader)); controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
controllerManagement.updateControllerAttributes(targetid, configData.getData()); controllerManagement.updateControllerAttributes(targetid, configData.getData());
@@ -493,7 +486,7 @@ public class RootController implements EnvironmentAware {
LOG.debug("getControllerCancelAction({})", targetid); LOG.debug("getControllerCancelAction({})", targetid);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, requestHeader)); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
final Action action = findActionWithExceptionIfNotFound(actionId); final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) { if (!action.getTarget().getId().equals(target.getId())) {
@@ -540,7 +533,7 @@ public class RootController implements EnvironmentAware {
LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback); LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, requestHeader)); IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader()));
if (!actionId.equals(feedback.getId())) { if (!actionId.equals(feedback.getId())) {
LOG.warn( LOG.warn(

View File

@@ -23,11 +23,6 @@ import org.springframework.data.domain.Sort.Direction;
/** /**
* Utility class for for paged body generation. * Utility class for for paged body generation.
* *
*
*
*
*
*
*/ */
public final class PagingUtility { public final class PagingUtility {
/* /*
@@ -90,8 +85,9 @@ public final class PagingUtility {
if (sortParam != null) { if (sortParam != null) {
sorting = new Sort(SortUtility.parse(ActionFields.class, sortParam)); sorting = new Sort(SortUtility.parse(ActionFields.class, sortParam));
} else { } else {
// default sort // default sort is DESC in case of action to match behavior
sorting = new Sort(Direction.ASC, ActionFields.ID.getFieldName()); // of management UI (last entry on top)
sorting = new Sort(Direction.DESC, ActionFields.ID.getFieldName());
} }
return sorting; return sorting;
} }
@@ -101,8 +97,9 @@ public final class PagingUtility {
if (sortParam != null) { if (sortParam != null) {
sorting = new Sort(SortUtility.parse(ActionStatusFields.class, sortParam)); sorting = new Sort(SortUtility.parse(ActionStatusFields.class, sortParam));
} else { } else {
// default sort // default sort is DESC in case of action status to match behavior
sorting = new Sort(Direction.ASC, ActionStatusFields.ID.getFieldName()); // of management UI (last entry on top)
sorting = new Sort(Direction.DESC, ActionStatusFields.ID.getFieldName());
} }
return sorting; return sorting;
} }

View File

@@ -297,8 +297,8 @@ final public class TargetMapper {
final ActionStatusRest result = new ActionStatusRest(); final ActionStatusRest result = new ActionStatusRest();
result.setMessages(actionStatus.getMessages()); result.setMessages(actionStatus.getMessages());
result.setReportedAt(action.getCreatedAt()); result.setReportedAt(actionStatus.getCreatedAt());
result.setStatusId(action.getId()); result.setStatusId(actionStatus.getId());
result.setType(getNameOfActionStatusType(actionStatus.getStatus())); result.setType(getNameOfActionStatusType(actionStatus.getStatus()));
return result; return result;

View File

@@ -235,7 +235,7 @@ public class TargetResource implements TargetRestApi {
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
final Sort sorting = PagingUtility.sanitizeActionStatusSortParam(sortParam); final Sort sorting = PagingUtility.sanitizeActionStatusSortParam(sortParam);
final Page<ActionStatus> statusList = this.deploymentManagement.findActionStatusMessagesByActionInDescOrder( final Page<ActionStatus> statusList = this.deploymentManagement.findActionStatusByAction(
new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting), action, true); new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting), action, true);
return new ResponseEntity<>( return new ResponseEntity<>(

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.rest.resource;
import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
@@ -34,6 +35,7 @@ import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.WithUser; import org.eclipse.hawkbit.WithUser;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -109,27 +111,24 @@ public class TargetResourceTest extends AbstractIntegrationTest {
new ActionStatus(actions.get(0), Status.FINISHED, System.currentTimeMillis(), "testmessage"), new ActionStatus(actions.get(0), Status.FINISHED, System.currentTimeMillis(), "testmessage"),
actions.get(0)); actions.get(0));
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionStatusFields.ID.getFieldName()); final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
final ActionStatus status = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(0).getActionStatus().stream().sorted((e1, e2) -> Long.compare(e2.getId(), e1.getId()))
.collect(Collectors.toList()).get(0);
// limit to 1 - first page -> standard cancel message
final Long reportAt = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(0).getCreatedAt();
final Long id = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(0).getId();
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId() + "/status") + RestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId() + "/status")
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize)) .param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize))
.param(RestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC")) .param(RestConstants.REQUEST_PARAMETER_SORTING, "ID:DESC"))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(3))) .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(3)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize))) .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize))) .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize)))
.andExpect(jsonPath("content.[0].id", equalTo(id.intValue()))) .andExpect(jsonPath("content.[0].id", equalTo(status.getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("finished"))) .andExpect(jsonPath("content.[0].type", equalTo("finished")))
.andExpect(jsonPath("content.[0].messages", hasSize(1))) .andExpect(jsonPath("content.[0].messages", hasSize(1)))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(reportAt))) .andExpect(jsonPath("content.[0].reportedAt", equalTo(status.getCreatedAt().longValue())))
.andExpect(jsonPath("content.[1].type", equalTo("canceling"))); .andExpect(jsonPath("content.[1].type", equalTo("canceling")));
} }
@@ -839,7 +838,8 @@ public class TargetResourceTest extends AbstractIntegrationTest {
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId); final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get( mvc.perform(get(
RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS)) RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS)
.param(RestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue()))) .andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("content.[1].type", equalTo("update"))) .andExpect(jsonPath("content.[1].type", equalTo("update")))
@@ -856,6 +856,108 @@ public class TargetResourceTest extends AbstractIntegrationTest {
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
} }
@Test
@Description("Verfies that the API returns the status list with expected content.")
public void getMultipleActionStatus() throws Exception {
final String knownTargetId = "targetId";
final Action action = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId).get(0);
// retrieve list in default descending order for actionstaus entries
final List<ActionStatus> actionStatus = action.getActionStatus().stream()
.sorted((e1, e2) -> Long.compare(e2.getId(), e1.getId())).collect(Collectors.toList());
// sort is default descending order, latest status first
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/" + RestConstants.TARGET_V1_ACTION_STATUS))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].id", equalTo(actionStatus.get(0).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("canceling")))
.andExpect(jsonPath("content.[0].messages", hasItem("manual cancelation requested")))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(actionStatus.get(0).getCreatedAt())))
.andExpect(jsonPath("content.[1].id", equalTo(actionStatus.get(1).getId().intValue())))
.andExpect(jsonPath("content.[1].type", equalTo("running")))
.andExpect(jsonPath("content.[1].reportedAt", equalTo(actionStatus.get(1).getCreatedAt())))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
}
@Test
@Description("Verfies that the API returns the status list with expected content sorted by reportedAt field.")
public void getMultipleActionStatusSortedByReportedAt() throws Exception {
final String knownTargetId = "targetId";
final Action action = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId).get(0);
final List<ActionStatus> actionStatus = action.getActionStatus().stream()
.sorted((e1, e2) -> Long.compare(e1.getId(), e2.getId())).collect(Collectors.toList());
// descending order
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/" + RestConstants.TARGET_V1_ACTION_STATUS)
.param(RestConstants.REQUEST_PARAMETER_SORTING, "REPORTEDAT:DESC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].id", equalTo(actionStatus.get(1).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("canceling")))
.andExpect(jsonPath("content.[0].messages", hasItem("manual cancelation requested")))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(actionStatus.get(1).getCreatedAt())))
.andExpect(jsonPath("content.[1].id", equalTo(actionStatus.get(0).getId().intValue())))
.andExpect(jsonPath("content.[1].type", equalTo("running")))
.andExpect(jsonPath("content.[1].reportedAt", equalTo(actionStatus.get(0).getCreatedAt())))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
// ascending order
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/" + RestConstants.TARGET_V1_ACTION_STATUS)
.param(RestConstants.REQUEST_PARAMETER_SORTING, "REPORTEDAT:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[1].id", equalTo(actionStatus.get(1).getId().intValue())))
.andExpect(jsonPath("content.[1].type", equalTo("canceling")))
.andExpect(jsonPath("content.[1].messages", hasItem("manual cancelation requested")))
.andExpect(jsonPath("content.[1].reportedAt", equalTo(actionStatus.get(1).getCreatedAt())))
.andExpect(jsonPath("content.[0].id", equalTo(actionStatus.get(0).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("running")))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(actionStatus.get(0).getCreatedAt())))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
}
@Test
@Description("Verfies that the API returns the status list with expected content split into two pages.")
public void getMultipleActionStatusWithPagingLimitRequestParameter() throws Exception {
final String knownTargetId = "targetId";
final Action action = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId).get(0);
final List<ActionStatus> actionStatus = action.getActionStatus().stream()
.sorted((e1, e2) -> Long.compare(e1.getId(), e2.getId())).collect(Collectors.toList());
// Page 1
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/" + RestConstants.TARGET_V1_ACTION_STATUS)
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1)))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].id", equalTo(actionStatus.get(1).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("canceling")))
.andExpect(jsonPath("content.[0].messages", hasItem("manual cancelation requested")))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(actionStatus.get(1).getCreatedAt())))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(1)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(1)));
// Page 2
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/" + RestConstants.TARGET_V1_ACTION_STATUS)
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1))
.param(RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1)))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].id", equalTo(actionStatus.get(0).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("running")))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(actionStatus.get(0).getCreatedAt())))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(1)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(1)));
}
@Test @Test
public void getMultipleActionsWithPagingLimitRequestParameter() throws Exception { public void getMultipleActionsWithPagingLimitRequestParameter() throws Exception {
final String knownTargetId = "targetId"; final String knownTargetId = "targetId";
@@ -864,7 +966,8 @@ public class TargetResourceTest extends AbstractIntegrationTest {
// page 1: one entry // page 1: one entry
mvc.perform(get( mvc.perform(get(
RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS) RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS)
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1))) .param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1))
.param(RestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue()))) .andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("cancel"))) .andExpect(jsonPath("content.[0].type", equalTo("cancel")))
@@ -879,7 +982,9 @@ public class TargetResourceTest extends AbstractIntegrationTest {
mvc.perform(get( mvc.perform(get(
RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS) RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS)
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1)) .param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1))
.param(RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1))) .param(RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1))
.param(RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1))
.param(RestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].id", equalTo(actions.get(1).getId().intValue()))) .andExpect(jsonPath("content.[0].id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("update"))) .andExpect(jsonPath("content.[0].type", equalTo("update")))
@@ -907,7 +1012,8 @@ public class TargetResourceTest extends AbstractIntegrationTest {
+ "?offset=0&limit=50&sort=id:DESC"; + "?offset=0&limit=50&sort=id:DESC";
} }
private List<Action> generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId) { private List<Action> generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId)
throws InterruptedException {
final PageRequest pageRequest = new PageRequest(0, 100, Direction.ASC, ActionStatusFields.ID.getFieldName()); final PageRequest pageRequest = new PageRequest(0, 100, Direction.ASC, ActionStatusFields.ID.getFieldName());
@@ -925,6 +1031,8 @@ public class TargetResourceTest extends AbstractIntegrationTest {
final List<Target> updatedTargets = deploymentManagement.assignDistributionSet(one, targets) final List<Target> updatedTargets = deploymentManagement.assignDistributionSet(one, targets)
.getAssignedTargets(); .getAssignedTargets();
// 2nd update // 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
deploymentManagement.assignDistributionSet(two, updatedTargets); deploymentManagement.assignDistributionSet(two, updatedTargets);
// two updates, one cancelation // two updates, one cancelation
@@ -951,54 +1059,6 @@ public class TargetResourceTest extends AbstractIntegrationTest {
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1))))); equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1)))));
} }
@Test
public void getActionStatusWithMultipleResultsWithPagingLimitRequestParameter() throws Exception {
final int limitSize = 1;
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
actions.get(0).setStatus(Status.RUNNING);
controllerManagament.addUpdateActionStatus(
new ActionStatus(actions.get(0), Status.RUNNING, System.currentTimeMillis(), "testmessage"),
actions.get(0));
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionStatusFields.ID.getFieldName());
// limit to 1 - first page -> standard cancel message
Long reportAt = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(0).getCreatedAt();
Long id = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(0).getId();
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId() + "/status")
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize)))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(3)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize)))
.andExpect(jsonPath("content.[0].id", equalTo(id.intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("running")))
.andExpect(jsonPath("content.[0].messages", hasSize(1)))
.andExpect(jsonPath("content.[0].reportedAt", equalTo(reportAt)));
// limit to 1 - first page -> added custom message
reportAt = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(1).getCreatedAt();
id = deploymentManagement
.findActionsByTarget(pageRequest, targetManagement.findTargetByControllerID(knownTargetId)).getContent()
.get(1).getCreatedAt();
mvc.perform(get(RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ RestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId() + "/status")
.param(RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize))
.param(RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1)))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(3)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(1)));
}
@Test @Test
public void assignDistributionSetToTarget() throws Exception { public void assignDistributionSetToTarget() throws Exception {

View File

@@ -24,7 +24,7 @@ hawkbit.server.database=H2
hawkbit.server.database.env=TEST hawkbit.server.database.env=TEST
spring.main.show_banner=false spring.main.show_banner=false
hawkbit.server.controller.security.authentication.header=true hawkbit.server.ddi.security.authentication.header=true
hawkbit.server.artifact.repo.upload.maxFileSize=5MB hawkbit.server.artifact.repo.upload.maxFileSize=5MB

View File

@@ -17,7 +17,7 @@
</Console> </Console>
</Appenders> </Appenders>
<Loggers> <Loggers>
<Logger name="org.eclipse.hawkbit.MockMvcResultPrinter" level="debug" /> <Logger name="org.eclipse.hawkbit.MockMvcResultPrinter" level="error" />
<Root level="error"> <Root level="error">
<AppenderRef ref="Console"/> <AppenderRef ref="Console"/>

View File

@@ -59,6 +59,11 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId> <artifactId>spring-boot</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test --> <!-- Test -->
<dependency> <dependency>

View File

@@ -0,0 +1,219 @@
/**
* 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
*/
package org.eclipse.hawkbit.security;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* The common properties for DDI security.
*/
@ConfigurationProperties("hawkbit.server.ddi.security")
public class DdiSecurityProperties {
private final Rp rp = new Rp();
private final Authentication authentication = new Authentication();
public Authentication getAuthentication() {
return authentication;
}
public Rp getRp() {
return rp;
}
/**
* Reverse proxy configuration. Defines the security properties for
* authenticating controllers behind a reverse proxy which terminates the
* SSL session at the reverse proxy but adding request header which contains
* the CN of the certificate.
*/
public static class Rp {
/**
* HTTP header field for common name of a DDI target client certificate.
*/
private String cnHeader = "X-Ssl-Client-Cn";
/**
* HTTP header field for issuer hash of a DDI target client certificate.
*/
private String sslIssuerHashHeader = "X-Ssl-Issuer-Hash-%d";
/**
* List of trusted (reverse proxy) IP addresses for performing DDI
* client certificate authentication.
*/
private List<String> trustedIPs;
/**
* @return the cnHeader
*/
public String getCnHeader() {
return cnHeader;
}
/**
* @param cnHeader
* the cnHeader to set
*/
public void setCnHeader(final String cnHeader) {
this.cnHeader = cnHeader;
}
/**
* @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;
}
}
/**
* DDI Authentication options.
*/
public static class Authentication {
private final Anonymous anonymous = new Anonymous();
private final Targettoken targettoken = new Targettoken();
private final Gatewaytoken gatewaytoken = new Gatewaytoken();
public Anonymous getAnonymous() {
return anonymous;
}
public Gatewaytoken getGatewaytoken() {
return gatewaytoken;
}
public Targettoken getTargettoken() {
return targettoken;
}
/**
* Target token authentication. Tokens are defined per target.
*
*/
public static class Targettoken {
/**
* Set to true to enable target token authentication.
*/
private boolean enabled = false;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
/**
* Gateway token authentication. Tokens are defined per tenant. Use with
* care!
*
*/
public static class Gatewaytoken {
/**
* Gateway token based authentication enabled.
*/
private boolean enabled = false;
/**
* Default gateway token name.
*/
private String name = "";
/**
* Default gateway token itself.
*/
private String key = "";
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
}
/**
* Anonymous authentication.
*/
public static class Anonymous {
/**
* Set to true to enable anonymous DDI client authentication.
*/
private boolean enabled = false;
/**
* @param enabled
* the enabled to set
*/
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
/**
* @return the enabled
*/
public boolean isEnabled() {
return enabled;
}
}
}
}

View File

@@ -0,0 +1,191 @@
/**
* 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
*/
package org.eclipse.hawkbit.security;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Security related hawkbit configuration.
*
*/
@Component
@ConfigurationProperties("hawkbit.server.security")
public class HawkbitSecurityProperties {
private final Clients clients = new Clients();
private final Dos dos = new Dos();
private final Xframe xframe = new Xframe();
public Dos getDos() {
return dos;
}
public Clients getClients() {
return clients;
}
public Xframe getXframe() {
return xframe;
}
/**
* Defines the XFrameOption policy.
*
*/
public static class Xframe {
/**
* XFrame option. Allowed values: SAMEORIGIN, DENY, ALLOW-FROM
*/
private String option = "DENY";
/**
* ALLOW-FROM defined URL, has to be filled in case ALLOW-FROM option is
* selected.
*/
private String allowfrom = "";
public String getOption() {
return option;
}
public void setOption(final String option) {
this.option = option;
}
public String getAllowfrom() {
return allowfrom;
}
public void setAllowfrom(final String allowfrom) {
this.allowfrom = allowfrom;
}
}
/**
* Security configuration related to clients.
*
*/
public static class Clients {
/**
* Blacklisted client (IP addresses) for for DDI and Management API.
*/
private String blacklist = "";
/**
* Name of the http header from which the remote ip is extracted.
*/
private String remoteIpHeader = "X-Forwarded-For";
public String getBlacklist() {
return blacklist;
}
public void setBlacklist(final String blacklist) {
this.blacklist = blacklist;
}
public String getRemoteIpHeader() {
return remoteIpHeader;
}
public void setRemoteIpHeader(final String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader;
}
}
/**
* Denial of service protection related properties.
*
*/
public static class Dos {
/**
* Maximum number of status updates that the controller can report for
* an action (0 to disable).
*/
private int maxStatusEntriesPerAction = 1000;
/**
* Maximum number of attributes that the controller can report;
*/
private int maxAttributeEntriesPerTarget = 100;
private final Filter filter = new Filter();
public Filter getFilter() {
return filter;
}
public int getMaxStatusEntriesPerAction() {
return maxStatusEntriesPerAction;
}
public void setMaxStatusEntriesPerAction(final int maxStatusEntriesPerAction) {
this.maxStatusEntriesPerAction = maxStatusEntriesPerAction;
}
public int getMaxAttributeEntriesPerTarget() {
return maxAttributeEntriesPerTarget;
}
public void setMaxAttributeEntriesPerTarget(final int maxAttributeEntriesPerTarget) {
this.maxAttributeEntriesPerTarget = maxAttributeEntriesPerTarget;
}
public static class Filter {
/**
* White list of peer IP addresses for DOS filter (regular
* expression).
*/
private String whitelist = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}";
/**
* # Maximum number of allowed REST read/GET requests per second per
* client.
*/
int maxRead = 200;
/**
* Maximum number of allowed REST write/(PUT/POST/etc.) requests per
* second per client.
*/
int maxWrite = 50;
public String getWhitelist() {
return whitelist;
}
public void setWhitelist(final String whitelist) {
this.whitelist = whitelist;
}
public int getMaxRead() {
return maxRead;
}
public void setMaxRead(final int maxRead) {
this.maxRead = maxRead;
}
public int getMaxWrite() {
return maxWrite;
}
public void setMaxWrite(final int maxWrite) {
this.maxWrite = maxWrite;
}
}
}
}

View File

@@ -1,130 +0,0 @@
/**
* 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
*/
package org.eclipse.hawkbit.security;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* The common properties for security.
*
*
*
*/
@ConfigurationProperties
public class SecurityProperties {
/**
* 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;
/**
* @return the cnHeader
*/
public String getCnHeader() {
return cnHeader;
}
/**
* @param cnHeader
* the cnHeader to set
*/
public void setCnHeader(final String cnHeader) {
this.cnHeader = cnHeader;
}
/**
* @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() {
return rppProperties.getCnHeader();
}
public String getRpSslIssuerHashHeader() {
return rppProperties.getSslIssuerHashHeader();
}
public List<String> getRpTrustedIPs() {
return rppProperties.getTrustedIPs();
}
public Boolean getAnonymousEnabled() {
return authenticationsProperties.getEnabled();
}
}

View File

@@ -11,90 +11,24 @@ package org.eclipse.hawkbit.util;
import javax.servlet.MultipartConfigElement; import javax.servlet.MultipartConfigElement;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* Bean which contains all informations about the SP software, e.g. like * Bean which contains all informations about the SP software, e.g. like
* version, built time etc. from the environment. * version, built time etc. from the environment.
* *
*
*
*
*/ */
@Component @Component
public class SPInfo implements EnvironmentAware { public class SPInfo {
// package private for testing purposes // package private for testing purposes
static final String UNKNOWN_VERSION = "unknown"; static final String UNKNOWN_VERSION = "unknown";
static final String UNKNOWN_CREDENTIAL = "unknown credential"; static final String UNKNOWN_CREDENTIAL = "unknown credential";
private Environment environmentData;
@Autowired @Autowired
private MultipartConfigElement configElement; private MultipartConfigElement configElement;
/*
* (non-Javadoc)
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@Override
public void setEnvironment(final Environment environment) {
this.environmentData = environment;
}
/**
* @return the version in string format, e.g. 1.0.0 or {@code "UNKNOWN"} in
* case the SP version info cannot be determined.
*/
public String getVersion() {
if (environmentData != null) {
return environmentData.getProperty("info.build.version", UNKNOWN_VERSION);
}
return UNKNOWN_VERSION;
}
public String getSupportEmail() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.email.support");
}
return "";
}
public String getRequestAccountEmail() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.email.request.account");
}
return "";
}
public String getDemoTenant() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.demo.tenant");
}
return UNKNOWN_CREDENTIAL;
}
public String getDemoUser() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.demo.user");
}
return UNKNOWN_CREDENTIAL;
}
public String getDemoPassword() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.demo.password");
}
return UNKNOWN_CREDENTIAL;
}
/** /**
* @return the max file size to upload artifact files in bytes which has * @return the max file size to upload artifact files in bytes which has
* been configured. * been configured.

View File

@@ -213,7 +213,6 @@
<groupId>org.vaadin.addons</groupId> <groupId>org.vaadin.addons</groupId>
<artifactId>tokenfield</artifactId> <artifactId>tokenfield</artifactId>
</dependency> </dependency>
<!-- To be mentioned in release notes -->
<dependency> <dependency>
<groupId>org.vaadin.alump.distributionbar</groupId> <groupId>org.vaadin.alump.distributionbar</groupId>
<artifactId>dbar-addon</artifactId> <artifactId>dbar-addon</artifactId>
@@ -222,7 +221,11 @@
<groupId>org.vaadin.addons</groupId> <groupId>org.vaadin.addons</groupId>
<artifactId>contextmenu</artifactId> <artifactId>contextmenu</artifactId>
</dependency> </dependency>
<!-- To be mentioned in release notes --> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test --> <!-- Test -->
<dependency> <dependency>

View File

@@ -0,0 +1,162 @@
/**
* 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
*/
package org.eclipse.hawkbit.ui;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Properties for Management UI customization.
*
*/
@Component
@ConfigurationProperties("hawkbit.server.ui")
public class UiProperties {
private final Links links = new Links();
private final Login login = new Login();
private final Demo demo = new Demo();
public Login getLogin() {
return login;
}
public Links getLinks() {
return links;
}
public Demo getDemo() {
return demo;
}
/**
* Demo account login information.
*
*/
public static class Demo {
/**
* Demo tenant.
*/
private String tenant = "DEFAULT";
/**
* Demo user name.
*/
private String user = "admin";
/**
* Demo user password.
*/
private String password = "admin";
public String getTenant() {
return tenant;
}
public void setTenant(final String tenant) {
this.tenant = tenant;
}
public String getUser() {
return user;
}
public void setUser(final String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
}
/**
* Links to potentially other systems (e.g. support, user management etc.).
*
*/
public static class Links {
/**
* Link to product support.
*/
private String support = "";
/**
* Link to request a system account, access.
*/
private String requestAccount = "";
/**
* Link to user management.
*/
private String userManagement = "";
public String getSupport() {
return support;
}
public void setSupport(final String support) {
this.support = support;
}
public String getRequestAccount() {
return requestAccount;
}
public void setRequestAccount(final String requestAccount) {
this.requestAccount = requestAccount;
}
public String getUserManagement() {
return userManagement;
}
public void setUserManagement(final String userManagement) {
this.userManagement = userManagement;
}
}
/**
* Configuration of login view.
*
*/
public static class Login {
private final Cookie cookie = new Cookie();
public Cookie getCookie() {
return cookie;
}
/**
* Cookie configuration for login credential cookie.
*
*/
public static class Cookie {
/**
* Secure cookie enabled.
*/
private boolean secure = true;
public boolean isSecure() {
return secure;
}
public void setSecure(final boolean secure) {
this.secure = secure;
}
}
}
}

View File

@@ -15,16 +15,14 @@ import javax.servlet.http.Cookie;
import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator; import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator;
import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationToken; import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationToken;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.documentation.DocumentationPageLink; import org.eclipse.hawkbit.ui.documentation.DocumentationPageLink;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider;
import org.eclipse.hawkbit.util.SPInfo;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
@@ -61,7 +59,7 @@ import com.vaadin.ui.themes.ValoTheme;
*/ */
@SpringView(name = "") @SpringView(name = "")
@UIScope @UIScope
public class LoginView extends VerticalLayout implements View, EnvironmentAware { public class LoginView extends VerticalLayout implements View {
private static final String LOGIN_TEXTFIELD = "login-textfield"; private static final String LOGIN_TEXTFIELD = "login-textfield";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(LoginView.class); private static final Logger LOGGER = LoggerFactory.getLogger(LoginView.class);
@@ -76,7 +74,7 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
private I18N i18n; private I18N i18n;
@Autowired @Autowired
private transient SPInfo spInfo; private transient UiProperties uiProperties;
@Autowired @Autowired
private transient MultitenancyIndicator multiTenancyIndicator; private transient MultitenancyIndicator multiTenancyIndicator;
@@ -86,9 +84,6 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
private PasswordField password; private PasswordField password;
private Button signin; private Button signin;
private Boolean secureCookie = Boolean.TRUE;
private String userManagementLoginUrl;
void loginAuthenticationFailedNotification() { void loginAuthenticationFailedNotification() {
final Notification notification = new Notification(i18n.get("notification.login.failed.title")); final Notification notification = new Notification(i18n.get("notification.login.failed.title"));
notification.setDescription(i18n.get("notification.login.failed.description")); notification.setDescription(i18n.get("notification.login.failed.description"));
@@ -119,7 +114,8 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
final URI spURI = Page.getCurrent().getLocation(); final URI spURI = Page.getCurrent().getLocation();
final String lookForDemoFragment = spURI.toString(); final String lookForDemoFragment = spURI.toString();
if (lookForDemoFragment.contains("?demo")) { if (lookForDemoFragment.contains("?demo")) {
login(spInfo.getDemoTenant(), spInfo.getDemoUser(), spInfo.getDemoPassword(), false); login(uiProperties.getDemo().getTenant(), uiProperties.getDemo().getUser(),
uiProperties.getDemo().getPassword(), false);
} }
final Component loginForm = buildLoginForm(); final Component loginForm = buildLoginForm();
@@ -243,18 +239,18 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
links.addComponent(demoLink); links.addComponent(demoLink);
demoLink.addStyleName(ValoTheme.LINK_SMALL); demoLink.addStyleName(ValoTheme.LINK_SMALL);
if (spInfo.getRequestAccountEmail() != null) { if (!uiProperties.getLinks().getRequestAccount().isEmpty()) {
final Link requestAccountLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_REQUESTACCOUNT, final Link requestAccountLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_REQUESTACCOUNT,
i18n.get("link.requestaccount.name"), spInfo.getRequestAccountEmail(), FontAwesome.SHOPPING_CART, i18n.get("link.requestaccount.name"), uiProperties.getLinks().getRequestAccount(),
"", linkStyle, true); FontAwesome.SHOPPING_CART, "", linkStyle, true);
links.addComponent(requestAccountLink); links.addComponent(requestAccountLink);
requestAccountLink.addStyleName(ValoTheme.LINK_SMALL); requestAccountLink.addStyleName(ValoTheme.LINK_SMALL);
} }
if (userManagementLoginUrl != null) { if (!uiProperties.getLinks().getUserManagement().isEmpty()) {
final Link userManagementLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_USERMANAGEMENT, final Link userManagementLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_USERMANAGEMENT,
i18n.get("link.usermanagement.name"), userManagementLoginUrl, FontAwesome.USERS, "_blank", i18n.get("link.usermanagement.name"), uiProperties.getLinks().getUserManagement(),
linkStyle, true); FontAwesome.USERS, "_blank", linkStyle, true);
links.addComponent(userManagementLink); links.addComponent(userManagementLink);
userManagementLink.addStyleName(ValoTheme.LINK_SMALL); userManagementLink.addStyleName(ValoTheme.LINK_SMALL);
} }
@@ -315,7 +311,7 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
// 100 days // 100 days
tenantCookie.setMaxAge(3600 * 24 * 100); tenantCookie.setMaxAge(3600 * 24 * 100);
tenantCookie.setHttpOnly(true); tenantCookie.setHttpOnly(true);
tenantCookie.setSecure(secureCookie); tenantCookie.setSecure(uiProperties.getLogin().getCookie().isSecure());
VaadinService.getCurrentResponse().addCookie(tenantCookie); VaadinService.getCurrentResponse().addCookie(tenantCookie);
} }
@@ -324,7 +320,7 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
// 100 days // 100 days
usernameCookie.setMaxAge(3600 * 24 * 100); usernameCookie.setMaxAge(3600 * 24 * 100);
usernameCookie.setHttpOnly(true); usernameCookie.setHttpOnly(true);
usernameCookie.setSecure(secureCookie); usernameCookie.setSecure(uiProperties.getLogin().getCookie().isSecure());
VaadinService.getCurrentResponse().addCookie(usernameCookie); VaadinService.getCurrentResponse().addCookie(usernameCookie);
} }
@@ -368,16 +364,4 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware
loginAuthenticationFailedNotification(); loginAuthenticationFailedNotification();
} }
} }
/*
* (non-Javadoc)
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@Override
public void setEnvironment(final Environment environment) {
secureCookie = environment.getProperty("hawkbit.server.ui.login.cookie.secure", Boolean.class, Boolean.TRUE);
userManagementLoginUrl = environment.getProperty("hawkbit.server.im.login.url", String.class);
}
} }

View File

@@ -16,6 +16,7 @@ import java.util.StringJoiner;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -43,6 +44,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod; import org.vaadin.spring.events.annotation.EventBusListenerMethod;
@@ -417,10 +420,10 @@ public class ActionHistoryTable extends TreeTable implements Handler {
final org.eclipse.hawkbit.repository.model.Action action = deploymentManagement final org.eclipse.hawkbit.repository.model.Action action = deploymentManagement
.findActionWithDetails(actionId); .findActionWithDetails(actionId);
final Pageable pageReq = new PageRequest(0, 1000); final Pageable pageReq = new PageRequest(0, 1000,
final Page<ActionStatus> actionStatusList = deploymentManagement new Sort(Direction.DESC, ActionStatusFields.ID.getFieldName()));
.findActionStatusMessagesByActionInDescOrder(pageReq, action, final Page<ActionStatus> actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, action,
managementUIState.isActionHistoryMaximized()); managementUIState.isActionHistoryMaximized());
final List<ActionStatus> content = actionStatusList.getContent(); final List<ActionStatus> content = actionStatusList.getContent();
/* /*
* Since the recent action status and messages are already * Since the recent action status and messages are already

View File

@@ -18,17 +18,16 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.eclipse.hawkbit.HawkbitServerProperties;
import org.eclipse.hawkbit.im.authentication.PermissionService; import org.eclipse.hawkbit.im.authentication.PermissionService;
import org.eclipse.hawkbit.im.authentication.UserPrincipal; import org.eclipse.hawkbit.im.authentication.UserPrincipal;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.documentation.DocumentationPageLink; import org.eclipse.hawkbit.ui.documentation.DocumentationPageLink;
import org.eclipse.hawkbit.ui.menu.DashboardEvent.PostViewChangeEvent; import org.eclipse.hawkbit.ui.menu.DashboardEvent.PostViewChangeEvent;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider;
import org.eclipse.hawkbit.util.SPInfo;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
@@ -42,7 +41,6 @@ import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope; import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Alignment; import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button; import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component; import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent; import com.vaadin.ui.CustomComponent;
@@ -50,7 +48,6 @@ import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label; import com.vaadin.ui.Label;
import com.vaadin.ui.Link; import com.vaadin.ui.Link;
import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.Command;
import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme; import com.vaadin.ui.themes.ValoTheme;
@@ -61,11 +58,17 @@ import com.vaadin.ui.themes.ValoTheme;
*/ */
@SpringComponent @SpringComponent
@UIScope @UIScope
public final class DashboardMenu extends CustomComponent implements EnvironmentAware { public final class DashboardMenu extends CustomComponent {
@Autowired @Autowired
private I18N i18n; private I18N i18n;
@Autowired
private transient UiProperties uiProperties;
@Autowired
private transient HawkbitServerProperties serverProperties;
private static final long serialVersionUID = 5394474618559481462L; private static final long serialVersionUID = 5394474618559481462L;
public static final String ID = "dashboard-menu"; public static final String ID = "dashboard-menu";
@@ -74,16 +77,12 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
private static final String STYLE_VISIBLE = "valo-menu-visible"; private static final String STYLE_VISIBLE = "valo-menu-visible";
// this should be resolved when we introduce event bus on UI to just inform // this should be resolved when we introduce event bus on UI to just inform
// the buttons directly // the buttons directly via events
// via events
private final List<ValoMenuItemButton> menuButtons = new ArrayList<>(); private final List<ValoMenuItemButton> menuButtons = new ArrayList<>();
@Autowired @Autowired
private transient PermissionService permissionService; private transient PermissionService permissionService;
@Autowired
private transient SPInfo spInfo;
@Autowired @Autowired
private final List<DashboardMenuItem> dashboardVaadinViews = new ArrayList<>(); private final List<DashboardMenuItem> dashboardVaadinViews = new ArrayList<>();
@@ -91,12 +90,10 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
private boolean accessibleViewsEmpty; private boolean accessibleViewsEmpty;
private String userManagementLoginUrl;
/** /**
* initializing the view and creating the layout, cannot be done in the * initializing the view and creating the layout, cannot be done in the
* custructor because the constructor will be called by spring and the * constructor because the constructor will be called by spring and the
* dashabord must be initialized when the dashboard UI is creating. * dashboard must be initialized when the dashboard UI is creating.
*/ */
public void init() { public void init() {
initialViewName = ""; initialViewName = "";
@@ -162,20 +159,20 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
links.addComponent(docuLink); links.addComponent(docuLink);
links.setComponentAlignment(docuLink, Alignment.BOTTOM_CENTER); links.setComponentAlignment(docuLink, Alignment.BOTTOM_CENTER);
if (userManagementLoginUrl != null) { if (!uiProperties.getLinks().getUserManagement().isEmpty()) {
final Link userManagementLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_USERMANAGEMENT, final Link userManagementLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_USERMANAGEMENT,
i18n.get("link.usermanagement.name"), userManagementLoginUrl, FontAwesome.USERS, "_blank", i18n.get("link.usermanagement.name"), uiProperties.getLinks().getUserManagement(),
linkStyle, true); FontAwesome.USERS, "_blank", linkStyle, true);
userManagementLink.setDescription(i18n.get("link.usermanagement.name")); userManagementLink.setDescription(i18n.get("link.usermanagement.name"));
links.addComponent(userManagementLink); links.addComponent(userManagementLink);
userManagementLink.setSizeFull(); userManagementLink.setSizeFull();
links.setComponentAlignment(userManagementLink, Alignment.BOTTOM_CENTER); links.setComponentAlignment(userManagementLink, Alignment.BOTTOM_CENTER);
} }
if (spInfo.getSupportEmail() != null) { if (!uiProperties.getLinks().getSupport().isEmpty()) {
final Link supportLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_SUPPORT, final Link supportLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_SUPPORT,
i18n.get("link.support.name"), spInfo.getSupportEmail(), FontAwesome.ENVELOPE_O, "", linkStyle, i18n.get("link.support.name"), uiProperties.getLinks().getSupport(), FontAwesome.ENVELOPE_O, "",
true); linkStyle, true);
supportLink.setDescription(i18n.get("link.support.name")); supportLink.setDescription(i18n.get("link.support.name"));
supportLink.setSizeFull(); supportLink.setSizeFull();
links.addComponent(supportLink); links.addComponent(supportLink);
@@ -222,12 +219,7 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
settingsItem.setDescription(user.getUsername()); settingsItem.setDescription(user.getUsername());
} }
settingsItem.addItem("Sign Out", new Command() { settingsItem.addItem("Sign Out", selectedItem -> Page.getCurrent().setLocation("/UI/logout"));
@Override
public void menuSelected(final MenuItem selectedItem) {
Page.getCurrent().setLocation("/UI/logout");
}
});
return settings; return settings;
} }
@@ -254,14 +246,11 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
} }
private Component buildToggleButton() { private Component buildToggleButton() {
final Button valoMenuToggleButton = new Button("Menu", new ClickListener() { final Button valoMenuToggleButton = new Button("Menu", (ClickListener) event -> {
@Override if (getCompositionRoot().getStyleName().contains(STYLE_VISIBLE)) {
public void buttonClick(final ClickEvent event) { getCompositionRoot().removeStyleName(STYLE_VISIBLE);
if (getCompositionRoot().getStyleName().contains(STYLE_VISIBLE)) { } else {
getCompositionRoot().removeStyleName(STYLE_VISIBLE); getCompositionRoot().addStyleName(STYLE_VISIBLE);
} else {
getCompositionRoot().addStyleName(STYLE_VISIBLE);
}
} }
}); });
valoMenuToggleButton.setIcon(FontAwesome.LIST); valoMenuToggleButton.setIcon(FontAwesome.LIST);
@@ -307,7 +296,7 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
final Label label = new Label(); final Label label = new Label();
label.setSizeFull(); label.setSizeFull();
label.setStyleName("version-layout"); label.setStyleName("version-layout");
label.setValue(spInfo.getVersion()); label.setValue(serverProperties.getBuild().getVersion());
return label; return label;
} }
@@ -341,11 +330,6 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
menuButtons.forEach(button -> button.postViewChange(event)); menuButtons.forEach(button -> button.postViewChange(event));
} }
@Override
public void setEnvironment(final Environment environment) {
userManagementLoginUrl = environment.getProperty("hawkbit.server.im.login.url", String.class);
}
/** /**
* Returns the dashboard view type by a given view name. * Returns the dashboard view type by a given view name.
* *
@@ -411,12 +395,7 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA
setDescription(view.getDashboardCaptionLong()); setDescription(view.getDashboardCaptionLong());
/* Avoid double click */ /* Avoid double click */
setDisableOnClick(true); setDisableOnClick(true);
addClickListener(new ClickListener() { addClickListener(event -> event.getComponent().getUI().getNavigator().navigateTo(view.getViewName()));
@Override
public void buttonClick(final ClickEvent event) {
event.getComponent().getUI().getNavigator().navigateTo(view.getViewName());
}
});
} }

View File

@@ -559,7 +559,6 @@
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>${json.version}</version> <version>${json.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>de.flapdoodle.embed</groupId> <groupId>de.flapdoodle.embed</groupId>