diff --git a/.gitignore b/.gitignore index ba9cf4617..8cd3d476e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,13 +16,13 @@ *.jar *.war -###################### # Sonar -###################### .sonar_lock -# Eclipse IDE +# Created by spring-boot-configuration-processor +.factorypath +# Eclipse IDE *.pydevproject .project .metadata diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 000000000..eacce864d --- /dev/null +++ b/MIGRATION.md @@ -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 diff --git a/examples/hawkbit-device-simulator/pom.xml b/examples/hawkbit-device-simulator/pom.xml index 9a84d13f5..94749789c 100644 --- a/examples/hawkbit-device-simulator/pom.xml +++ b/examples/hawkbit-device-simulator/pom.xml @@ -100,7 +100,6 @@ com.google.guava guava - 19.0 com.netflix.feign @@ -116,6 +115,11 @@ com.jayway.jsonpath json-path + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java index ff9762c5d..f9e6ab23d 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java @@ -9,21 +9,35 @@ package org.eclipse.hawkbit.simulator.amqp; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; /** * Bean which holds the necessary properties for configuring the AMQP * connection. * - * - * */ +@Component @ConfigurationProperties("hawkbit.device.simulator.amqp") public class AmqpProperties { + /** + * Queue for receiving DMF messages from update server. + */ private String receiverConnectorQueueFromSp; + + /** + * Exchange for sending DMF messages to update server. + */ private String senderForSpExchange; + /** + * Simulator dead letter queue. + */ private String deadLetterQueue; + + /** + * Simulator dead letter exchange. + */ private String deadLetterExchange; public String getReceiverConnectorQueueFromSp() { diff --git a/examples/hawkbit-example-app/src/main/resources/application.properties b/examples/hawkbit-example-app/src/main/resources/application.properties index 13ceca40a..d3eddeff1 100644 --- a/examples/hawkbit-example-app/src/main/resources/application.properties +++ b/examples/hawkbit-example-app/src/main/resources/application.properties @@ -7,23 +7,20 @@ # http://www.eclipse.org/legal/epl-v10.html # -# need to re-name these properties in the defaulthawkbit.properties and code! -hawkbit.server.controller.security.authentication.anonymous.enabled=true -hawkbit.server.controller.security.authentication.header.enabled=false -hawkbit.server.controller.security.authentication.targettoken.enabled=false -hawkbit.server.controller.security.authentication.gatewaytoken.enabled=false +hawkbit.server.ddi.security.authentication.anonymous.enabled=true +hawkbit.server.ddi.security.authentication.targettoken.enabled=false +hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false spring.profiles.active=amqp vaadin.servlet.productionMode=false -vaadin.static.servlet.productionMode=false ## Configuration for RabbitMQ integration -hawkbit.server.amqp.username=guest -hawkbit.server.amqp.password=guest -hawkbit.server.amqp.virtualHost=/ -hawkbit.server.amqp.host=localhost -hawkbit.server.amqp.port=5672 -hawkbit.server.amqp.deadLetterQueue=sp_deadletter -hawkbit.server.amqp.deadLetterExchange=sp.deadletter -hawkbit.server.amqp.receiverQueue=sp_receiver +spring.rabbitmq.username=guest +spring.rabbitmq.password=guest +spring.rabbitmq.virtualHost=/ +spring.rabbitmq.host=localhost +spring.rabbitmq.port=5672 +hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter +hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter +hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver diff --git a/examples/hawkbit-mgmt-api-client/pom.xml b/examples/hawkbit-mgmt-api-client/pom.xml index 6e62bfe4e..9aaf53dc6 100644 --- a/examples/hawkbit-mgmt-api-client/pom.xml +++ b/examples/hawkbit-mgmt-api-client/pom.xml @@ -87,5 +87,10 @@ google-collections 1.0-rc2 + + org.springframework.boot + spring-boot-configuration-processor + true + \ No newline at end of file diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java index 6d15bcc04..ead019247 100644 --- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java +++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java @@ -9,17 +9,30 @@ package org.eclipse.hawkbit.mgmt.client; 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 * URL of the hawkbit-server and the credentials to use the RESTful Management * API. */ +@Component @ConfigurationProperties(prefix = "hawkbit") public class ClientConfigurationProperties { + /** + * Update server URI. + */ private String url = "localhost:8080"; + + /** + * Update server user name. + */ private String username = "admin"; + + /** + * Update server password. + */ private String password = "admin"; // NOSONAR this password is only used for // examples diff --git a/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties b/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties index da0aa79dd..d3a3eb969 100644 --- a/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties +++ b/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties @@ -11,4 +11,4 @@ hawkbit.url=localhost:8080 hawkbit.username=admin hawkbit.password=admin -spring.main.banner-mode=OFF \ No newline at end of file +spring.main.show-banner=false \ No newline at end of file diff --git a/hawkbit-autoconfigure/pom.xml b/hawkbit-autoconfigure/pom.xml index 11a106a4b..7670da406 100644 --- a/hawkbit-autoconfigure/pom.xml +++ b/hawkbit-autoconfigure/pom.xml @@ -72,5 +72,10 @@ org.springframework spring-context-support + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/conf/ControllerPollAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/conf/ControllerPollAutoConfiguration.java deleted file mode 100644 index bcbc5ec16..000000000 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/conf/ControllerPollAutoConfiguration.java +++ /dev/null @@ -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 { - -} diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java index f7f18f1b0..35996a114 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java @@ -13,17 +13,29 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** * Properties for the async configurer. * - * */ @ConfigurationProperties("hawkbit.threadpool") public class AsyncConfigurerThreadpoolProperties { + /** + * Max queue size for central event executor. + */ private Integer queuesize = 250; + /** + * Core processing threads for central event executor. + */ private Integer corethreads = 5; + /** + * Maximum thread pool size for central event executor. + */ 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; public Integer getQueuesize() { diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java index f8a86d1bc..43a096e7d 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java @@ -26,10 +26,9 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor import com.google.common.util.concurrent.ThreadFactoryBuilder; /** - * + * Central event processors inside update server. * */ - @Configuration @EnableConfigurationProperties(AsyncConfigurerThreadpoolProperties.class) public class ExecutorAutoConfiguration { @@ -40,7 +39,7 @@ public class ExecutorAutoConfiguration { private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties; /** - * @return ExecutorService for general pupose multi threaded operations + * @return ExecutorService for general purpose multi threaded operations */ @Bean @ConditionalOnMissingBean diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java index 326ff4774..8a3a50ad2 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java @@ -20,7 +20,7 @@ import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter; 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.tenancy.TenantAware; import org.slf4j.Logger; @@ -53,7 +53,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi * */ @Configuration -@EnableConfigurationProperties(SecurityProperties.class) +@EnableConfigurationProperties(DdiSecurityProperties.class) public class SecurityAutoConfiguration { /** diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java index ca55f1711..b27e76a28 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java @@ -31,29 +31,27 @@ import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.rest.resource.RestConstants; import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSource; +import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.DosFilter; +import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter; import org.eclipse.hawkbit.security.HttpDownloadAuthenticationFilter; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; -import org.eclipse.hawkbit.security.SecurityProperties; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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.ServletListenerRegistrationBean; import org.springframework.cache.Cache; -import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; 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) @EnableWebMvcSecurity @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 String SP_SERVER_CONFIG_PREFIX = "hawkbit.server."; - private RelaxedPropertyResolver environment; - - @Override - public void setEnvironment(final Environment environment) { - this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX); - - } + @Autowired + private HawkbitSecurityProperties securityProperties; /** * {@link WebSecurityConfigurer} for the internal SP controller API. @@ -123,7 +115,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { @Autowired private TenantAware tenantAware; @Autowired - private SecurityProperties securityConfiguration; + private DdiSecurityProperties ddiSecurityConfiguration; @Autowired private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; @@ -132,7 +124,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource(); final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter( - securityConfiguration.getRpCnHeader(), securityConfiguration.getRpSslIssuerHashHeader(), + ddiSecurityConfiguration.getRp().getCnHeader(), ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), systemManagement, tenantAware); securityHeaderFilter.setAuthenticationManager(authenticationManager()); securityHeaderFilter.setCheckForPrincipalChanges(true); @@ -158,7 +150,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); } - if (securityConfiguration.getAnonymousEnabled()) { + if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) { LOG.info( "******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************"); final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter( @@ -189,7 +181,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { @Override protected void configure(final AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider( - new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs())); + new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs())); } } @@ -204,13 +196,10 @@ public class SecurityManagedConfiguration implements EnvironmentAware { public FilterRegistrationBean dosFilter() { final FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); - filterRegBean - .setFilter( - new DosFilter(environment.getProperty("security.dos.filter.maxRead", Integer.class, 200), - environment.getProperty("security.dos.filter.maxWrite", Integer.class, 50), - environment.getProperty("security.dos.filter.whitelist"), environment - .getProperty("security.clients.blacklist"), - environment.getProperty("security.rp.remote_ip_header", String.class, "X-Forwarded-For"))); + filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(), + securityProperties.getDos().getFilter().getMaxWrite(), + securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(), + securityProperties.getClients().getRemoteIpHeader())); filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*"); return filterRegBean; } @@ -310,8 +299,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { @Configuration @Order(400) @EnableVaadinSecurity - public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter - implements EnvironmentAware { + public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { private static final String XFRAME_OPTION_DENY = "DENY"; private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN"; @@ -320,13 +308,8 @@ public class SecurityManagedConfiguration implements EnvironmentAware { private VaadinSecurityContext vaadinSecurityContext; @Autowired private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; - - private RelaxedPropertyResolver environment; - - @Override - public void setEnvironment(final Environment environment) { - this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX); - } + @Autowired + private HawkbitSecurityProperties securityProperties; /** * 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 { // configuration xframe-option - final String confXframeOption = environment.getProperty("security.xframe.option", XFRAME_OPTION_DENY); - final String confAllowFromUri = environment.getProperty("security.xframe.option.allowfrom"); - if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri == null) { + final String confXframeOption = securityProperties.getXframe().getOption(); + final String confAllowFromUri = securityProperties.getXframe().getAllowfrom(); + if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri.isEmpty()) { // if allow-from option is specified but no allowFromUri throw // exception 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"); } @@ -461,7 +444,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { @Autowired - private SecurityProperties securityConfiguration; + private DdiSecurityProperties ddiSecurityConfiguration; @Autowired @Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) @@ -484,7 +467,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware { @Override protected void configure(final AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider( - new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs())); + new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs())); } } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java index eb925e281..0bd4a8240 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.autoconfigure.url; import java.net.MalformedURLException; import java.net.URL; +import org.eclipse.hawkbit.HawkbitServerProperties; import org.eclipse.hawkbit.api.HostnameResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -27,11 +28,11 @@ import com.google.common.base.Throwables; * */ @Configuration -@EnableConfigurationProperties(ServerProperties.class) +@EnableConfigurationProperties(HawkbitServerProperties.class) public class PropertyHostnameResolverAutoConfiguration { @Autowired - private ServerProperties serverProperties; + private HawkbitServerProperties serverProperties; /** * @return the default autoconfigure hostname resolver implementation which diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/ServerProperties.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/ServerProperties.java deleted file mode 100644 index 24c9dfb0e..000000000 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/ServerProperties.java +++ /dev/null @@ -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; - } -} diff --git a/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories b/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories index 5d8d005d9..335054585 100644 --- a/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories @@ -11,5 +11,4 @@ org.eclipse.hawkbit.autoconfigure.eventbus.EventBusAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.cache.RedisAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\ -org.eclipse.hawkbit.autoconfigure.conf.ControllerPollAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration diff --git a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties index 98749a384..8f7ab288a 100644 --- a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties +++ b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties @@ -7,12 +7,6 @@ # 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 spring.jpa.eclipselink.eclipselink.weaving=false spring.jpa.database=H2 @@ -21,7 +15,6 @@ spring.datasource.driverClassName=org.h2.Driver # MongoDB for artifact-repository spring.data.mongodb.uri=mongodb://localhost/artifactrepo -spring.data.mongo.repositories.enabled=true # Flyway DDL flyway.enabled=true @@ -29,15 +22,10 @@ flyway.initOnMigrate=true flyway.sqlMigrationSuffix=${spring.jpa.database}.sql # Vaadin Servlet -vaadin.static.servlet.productionMode=true vaadin.servlet.productionMode=true vaadin.servlet.urlMapping=/UI/* -vaadin.servlet.params.heartbeatInterval=60 -vaadin.servlet.params.closeIdleSessions=false - -# Spring MVC -spring.mvc.favicon.enabled=false - +vaadin.servlet.heartbeatInterval=60 +vaadin.servlet.closeIdleSessions=false # Defines the thread pool executor hawkbit.threadpool.corethreads=5 @@ -50,6 +38,6 @@ hawkbit.controller.pollingTime=00:05:00 hawkbit.controller.pollingOverdueTime=00:05:00 ## Configuration for RabbitMQ integration -hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter -hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter +hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_receiver_deadletter +hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.receiver.deadletter hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver diff --git a/hawkbit-cache-redis/pom.xml b/hawkbit-cache-redis/pom.xml index 99c8328b5..09567291b 100644 --- a/hawkbit-cache-redis/pom.xml +++ b/hawkbit-cache-redis/pom.xml @@ -37,6 +37,11 @@ redis.clients jedis + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java index acf50ad3f..edc183b17 100644 --- a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java +++ b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java @@ -26,8 +26,6 @@ import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer * The spring Redis configuration which is enabled by using the profile * {@code redis} to use a Redis server as cache. * - * - * */ @Configuration @EnableConfigurationProperties(RedisProperties.class) diff --git a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java index c228cde4c..ab409bbf5 100644 --- a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java +++ b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java @@ -14,14 +14,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * Bean which holds the necessary properties for configuring the Redis * connection. * - * - * - * */ @ConfigurationProperties("hawkbit.server.redis") public class RedisProperties { + /** + * Redis server hostname. + */ private String host; + + /** + * Redis server port. + */ private int port; /** diff --git a/hawkbit-core/pom.xml b/hawkbit-core/pom.xml index b56d30075..f9e140d40 100644 --- a/hawkbit-core/pom.xml +++ b/hawkbit-core/pom.xml @@ -43,6 +43,11 @@ allure-junit-adaptor test + + org.springframework.boot + spring-boot-configuration-processor + true + \ No newline at end of file diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java index 694d5c016..4e176d258 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java @@ -9,18 +9,26 @@ package org.eclipse.hawkbit; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; /** * Defines the polling time for the controllers in HH:MM:SS notation. * - * - * */ - +@Component @ConfigurationProperties(prefix = "hawkbit.controller") public class ControllerPollProperties { + /** + * Recommended target polling time for DDI API. Final choice is up to the + * target. + */ 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"; public String getPollingTime() { diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitServerProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitServerProperties.java new file mode 100644 index 000000000..878965102 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitServerProperties.java @@ -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; + } +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java index 22fa42474..ef8bf3c98 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java @@ -20,7 +20,12 @@ public enum ActionStatusFields implements FieldNameProvider { /** * The id field. */ - ID("id"); + ID("id"), + + /** + * The reportedAt field. + */ + REPORTEDAT("createdAt"); private final String fieldName; diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java index 477cd654e..ec5c5ec40 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java @@ -22,35 +22,35 @@ public enum TenantConfigurationKey { * boolean value {@code true} {@code false}. */ 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", - "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}. */ 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}. */ 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. */ 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 * token. */ 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 defaultKeyName; diff --git a/hawkbit-dmf-amqp/pom.xml b/hawkbit-dmf-amqp/pom.xml index a7dfc5b42..2fded8559 100644 --- a/hawkbit-dmf-amqp/pom.xml +++ b/hawkbit-dmf-amqp/pom.xml @@ -60,6 +60,11 @@ org.slf4j slf4j-api + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java index 9b98cadfa..58ad38c92 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java @@ -21,9 +21,9 @@ import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter; +import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; import org.eclipse.hawkbit.security.PreAuthenficationFilter; -import org.eclipse.hawkbit.security.SecurityProperties; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,7 +55,7 @@ public class AmqpControllerAuthentfication { private TenantAware tenantAware; @Autowired - private SecurityProperties secruityProperties; + private DdiSecurityProperties ddiSecruityProperties; /** * Constructor. @@ -78,8 +78,8 @@ public class AmqpControllerAuthentfication { filterChain.add(gatewaySecurityTokenFilter); final ControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new ControllerPreAuthenticatedSecurityHeaderFilter( - secruityProperties.getRpCnHeader(), secruityProperties.getRpSslIssuerHashHeader(), systemManagement, - tenantAware); + ddiSecruityProperties.getRp().getCnHeader(), ddiSecruityProperties.getRp().getSslIssuerHashHeader(), + systemManagement, tenantAware); filterChain.add(securityHeaderFilter); final ControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new ControllerPreAuthenticateSecurityTokenFilter( @@ -134,8 +134,8 @@ public class AmqpControllerAuthentfication { this.controllerManagement = controllerManagement; } - public void setSecruityProperties(final SecurityProperties secruityProperties) { - this.secruityProperties = secruityProperties; + public void setSecruityProperties(final DdiSecurityProperties secruityProperties) { + this.ddiSecruityProperties = secruityProperties; } public void setSystemManagement(final SystemManagement systemManagement) { diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java index c3a807f48..4b33a59b8 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java @@ -8,41 +8,91 @@ */ package org.eclipse.hawkbit.amqp; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; /** * Bean which holds the necessary properties for configuring the AMQP * connection. - * + * */ +@Component @ConfigurationProperties("hawkbit.dmf.rabbitmq") public class AmqpProperties { - + /** + * DMF API dead letter queue. + */ private String deadLetterQueue = "dmf_receiver_deadletter"; + + /** + * DMF API dead letter exchange. + */ private String deadLetterExchange = "dmf.receiver.deadletter"; + + /** + * DMF API receiving queue. + */ private String receiverQueue = "dmf_receiver"; + + /** + * Missing queue fatal. + */ private boolean missingQueuesFatal = false; + /** + * Is missingQueuesFatal enabled + * + * @see SimpleMessageListenerContainer#setMissingQueuesFatal + * @return the missingQueuesFatal enabled disabled + */ public boolean isMissingQueuesFatal() { return missingQueuesFatal; } + /** + * @param missingQueuesFatal + * the missingQueuesFatal to set. + * @see SimpleMessageListenerContainer#setMissingQueuesFatal + */ public void setMissingQueuesFatal(final boolean missingQueuesFatal) { this.missingQueuesFatal = missingQueuesFatal; } + /** + * Returns the dead letter exchange. + * + * @return dead letter exchange + */ public String getDeadLetterExchange() { return deadLetterExchange; } + /** + * Sets the dead letter exchange. + * + * @param deadLetterExchange + * the deadLetterExchange to be set + */ public void setDeadLetterExchange(final String deadLetterExchange) { this.deadLetterExchange = deadLetterExchange; } + /** + * Returns the dead letter queue. + * + * @return the dead letter queue + */ public String getDeadLetterQueue() { return deadLetterQueue; } + /** + * Sets the dead letter queue. + * + * @param deadLetterQueue + * the deadLetterQueue ro be set + */ public void setDeadLetterQueue(final String deadLetterQueue) { this.deadLetterQueue = deadLetterQueue; } diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java index af5ce3267..501fddf81 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java @@ -23,8 +23,9 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; 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.SecurityProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.junit.Before; import org.junit.Test; @@ -67,8 +68,11 @@ public class AmqpControllerAuthenticationTest { authenticationManager = new AmqpControllerAuthentfication(); 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); systemManagement = mock(SystemManagement.class); authenticationManager.setSystemManagement(systemManagement); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java index 169a12f1e..e7ba06d19 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java @@ -56,7 +56,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest } @Test - @Description("Tests generate the http download url") + @Description("Tests the generation of http download url.") public void testHttpUrl() { final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTP); assertEquals("http is build incorrect", @@ -67,7 +67,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest } @Test - @Description("Tests generate the https download url") + @Description("Tests the generation of https download url.") public void testHttpsUrl() { final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTPS); assertEquals("https is build incorrect", @@ -78,7 +78,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest } @Test - @Description("Tests generate the coap download url") + @Description("Tests the generation of coap download url.") public void testCoapUrl() { final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.COAP); diff --git a/hawkbit-repository/pom.xml b/hawkbit-repository/pom.xml index 981da979e..7259262de 100644 --- a/hawkbit-repository/pom.xml +++ b/hawkbit-repository/pom.xml @@ -99,6 +99,11 @@ org.flywaydb flyway-core + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index e2d31a04c..84d1d9b60 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -40,10 +40,6 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess /** * General configuration for the SP Repository. * - * - * - * - * */ @EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository" }) @EnableTransactionManagement diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RolloutProperties.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RolloutProperties.java new file mode 100644 index 000000000..63e116f47 --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RolloutProperties.java @@ -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; + } + + } + +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java index 5e2800755..2705b9ac6 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java @@ -21,45 +21,47 @@ import org.springframework.transaction.annotation.Transactional; /** * {@link ActionStatus} repository. * - * - * - * */ @Transactional(readOnly = true) public interface ActionStatusRepository extends BaseEntityRepository, JpaSpecificationExecutor { /** - * @param target + * Counts {@link ActionStatus} entries of given {@link Action} in + * repository. + * * @param action - * @return + * to count status entries + * @return number of actions in repository */ Long countByAction(Action action); /** + * Counts {@link ActionStatus} entries of given {@link Action} with given + * {@link Status} in repository. + * * @param action - * @param retrieved - * @return + * to count status entries + * @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 + * parameters * @param action - * @return + * of the status entries + * @return pages list of {@link ActionStatus} entries */ Page findByAction(Pageable pageReq, Action action); /** - * @param pageReq - * @param action - * @return - */ - Page findByActionOrderByIdDesc(Pageable pageReq, Action action); - - /** - * Finds all status updates for the defined action and target order by - * {@link ActionStatus#getId()} desc including + * Finds all status updates for the defined action and target including * {@link ActionStatus#getMessages()}. * * @param pageReq @@ -71,6 +73,6 @@ public interface ActionStatusRepository * @return Page with found targets */ @EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD) - Page getByActionOrderByIdDesc(Pageable pageReq, Action action); + Page getByAction(Pageable pageReq, Action action); } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index e4a1e5e6b..b6fbb6010 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -33,13 +33,11 @@ import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.Target_; +import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.hibernate.validator.constraints.NotEmpty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.repository.Modifying; import org.springframework.security.access.prepost.PreAuthorize; @@ -57,7 +55,7 @@ import org.springframework.validation.annotation.Validated; @Transactional(readOnly = true) @Validated @Service -public class ControllerManagement implements EnvironmentAware { +public class ControllerManagement { private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class); private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); @@ -85,9 +83,8 @@ public class ControllerManagement implements EnvironmentAware { @Autowired private ActionStatusRepository actionStatusRepository; - private Integer maxCount = 1000; - - private Integer maxAttributes = 100; + @Autowired + private HawkbitSecurityProperties securityProperties; /** * 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) { - if (maxCount > 0) { + if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) { final Long statusCount = actionStatusRepository.countByAction(action); - if (statusCount >= maxCount) { + if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) { LOG_DOS.error( "Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!", - maxCount); - throw new ToManyStatusEntriesException(String.valueOf(maxCount)); + securityProperties.getDos().getMaxStatusEntriesPerAction()); + throw new ToManyStatusEntriesException( + String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction())); } } } @@ -436,10 +434,12 @@ public class ControllerManagement implements EnvironmentAware { 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!", - maxAttributes); - throw new ToManyAttributeEntriesException(String.valueOf(maxAttributes)); + securityProperties.getDos().getMaxAttributeEntriesPerTarget()); + throw new ToManyAttributeEntriesException( + String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget())); } target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis()); @@ -447,19 +447,6 @@ public class ControllerManagement implements EnvironmentAware { 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 * it does not exist yet. diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index 58ce8c26d..e1976c9a6 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -925,7 +925,7 @@ public class DeploymentManagement { /** * 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 * pagination parameter @@ -937,12 +937,12 @@ public class DeploymentManagement { * @return the corresponding {@link Page} of {@link ActionStatus} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Page findActionStatusMessagesByActionInDescOrder(final Pageable pageReq, final Action action, + public Page findActionStatusByAction(final Pageable pageReq, final Action action, final boolean withMessages) { if (withMessages) { - return actionStatusRepository.getByActionOrderByIdDesc(pageReq, action); + return actionStatusRepository.getByAction(pageReq, action); } else { - return actionStatusRepository.findByActionOrderByIdDesc(pageReq, action); + return actionStatusRepository.findByAction(pageReq, action); } } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java index 8f87f9209..eab926b4b 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java @@ -46,7 +46,6 @@ import org.eclipse.hawkbit.repository.model.Target_; import org.eclipse.hawkbit.tenancy.TenantAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -55,14 +54,10 @@ import org.springframework.validation.annotation.Validated; /** * Service layer for generating SP reportings. * - * - * - * */ @Transactional(readOnly = true) @Validated @Service -@ConfigurationProperties public class ReportManagement { @Value("${spring.jpa.database}") diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java index b60d64cc5..24b7c2627 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java @@ -10,14 +10,13 @@ package org.eclipse.hawkbit.repository; import java.util.List; +import org.eclipse.hawkbit.RolloutProperties; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Profile; -import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.Scheduled; 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 // rolloutmanagement and leads weird side-effects maybe. @Profile("!test") -public class RolloutScheduler implements EnvironmentAware { +public class RolloutScheduler { 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 private TenantAware tenantAware; @@ -52,7 +46,8 @@ public class RolloutScheduler implements EnvironmentAware { @Autowired private SystemSecurityContext systemSecurityContext; - private long fixedDelay = DEFAULT_SCHEDULER_DELAY; + @Autowired + private RolloutProperties rolloutProperties; /** * 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 * {@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() { logger.debug("rollout schedule checker has been triggered."); // 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()); for (final String tenant : tenants) { tenantAware.runAsTenant(tenant, () -> { - rolloutManagement.checkRunningRollouts(fixedDelay); + rolloutManagement.checkRunningRollouts(rolloutProperties.getScheduler().getFixedDelay()); return null; }); } return null; }); } - - @Override - public void setEnvironment(final Environment environment) { - fixedDelay = environment.getProperty(PROP_SCHEDULER_DELAY, Long.class, DEFAULT_SCHEDULER_DELAY); - } } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestConfiguration.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestConfiguration.java index 945e71c75..706cb4479 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestConfiguration.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestConfiguration.java @@ -17,8 +17,8 @@ import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.repository.model.helper.EventBusHolder; import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator; 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.SecurityProperties; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware; import org.eclipse.hawkbit.tenancy.TenantAware; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; @@ -47,7 +47,7 @@ import com.mongodb.MongoClientOptions; */ @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true) -@EnableConfigurationProperties({ SecurityProperties.class, ControllerPollProperties.class }) +@EnableConfigurationProperties({ DdiSecurityProperties.class, ControllerPollProperties.class }) @Profile("test") public class TestConfiguration implements AsyncConfigurer { diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java index 41a7c7848..a3913da2b 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java @@ -70,8 +70,8 @@ public class ControllerManagementTest extends AbstractIntegrationTest { .isEqualTo(TargetUpdateStatus.IN_SYNC); assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3); - assertThat(deploymentManagement.findActionStatusMessagesByActionInDescOrder(pageReq, savedAction, false) - .getNumberOfElements()).isEqualTo(3); + assertThat(deploymentManagement.findActionStatusByAction(pageReq, savedAction, false).getNumberOfElements()) + .isEqualTo(3); } @Test diff --git a/hawkbit-repository/src/test/resources/application-test.properties b/hawkbit-repository/src/test/resources/application-test.properties index e5fb04a21..dc7549fcb 100644 --- a/hawkbit-repository/src/test/resources/application-test.properties +++ b/hawkbit-repository/src/test/resources/application-test.properties @@ -10,7 +10,7 @@ spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value} 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 @@ -29,11 +29,6 @@ flyway.initOnMigrate=true flyway.sqlMigrationSuffix=${spring.jpa.database}.sql #spring.jpa.show-sql=true -# SP Controller configuration +# DDI configuration hawkbit.controller.pollingTime=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 +hawkbit.controller.pollingOverdueTime=00:01:00 \ No newline at end of file diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java index 05fd6c492..c2dbd3ba5 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java @@ -25,13 +25,11 @@ import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper; +import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.util.IpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.ResponseEntity; import org.springframework.security.web.bind.annotation.AuthenticationPrincipal; @@ -55,7 +53,7 @@ import org.springframework.web.bind.annotation.RestController; */ @RestController @RequestMapping(ControllerConstants.ARTIFACTS_V1_REQUEST_MAPPING) -public class ArtifactStoreController implements EnvironmentAware { +public class ArtifactStoreController { private static final Logger LOG = LoggerFactory.getLogger(ArtifactStoreController.class); @Autowired @@ -67,14 +65,8 @@ public class ArtifactStoreController implements EnvironmentAware { @Autowired private CacheWriteNotify cacheWriteNotify; - private static final String SP_SERVER_CONFIG_PREFIX = "hawkbit.server."; - private RelaxedPropertyResolver environment; - - @Override - public void setEnvironment(final Environment environment) { - this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX); - - } + @Autowired + private HawkbitSecurityProperties securityProperties; /** * 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, final LocalArtifact artifact) { - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil.getClientIpFromRequest( - request, environment.getProperty("security.rp.remote_ip_header", String.class, "X-Forwarded-For"))); + final Target target = controllerManagement.updateLastTargetQuery(targetid, + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); final Action action = controllerManagement .getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), artifact.getSoftwareModule()); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java index 2b0ac810e..6928ee7b5 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java @@ -41,15 +41,13 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper; +import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.util.IpUtil; import org.hibernate.validator.constraints.NotEmpty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.MediaType; import org.springframework.http.ResponseEntity; @@ -74,7 +72,7 @@ import org.springframework.web.bind.annotation.RestController; */ @RestController @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 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 private TenantAware tenantAware; - private String requestHeader; - - @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"); - } + @Autowired + private HawkbitSecurityProperties securityProperties; /** * Returns all artifacts of a given software module and target. @@ -155,12 +145,13 @@ public class RootController implements EnvironmentAware { LOG.debug("getControllerBase({})", targetid); final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, - IpUtil.getClientIpFromRequest(request, requestHeader)); + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) { LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid); controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED, - System.currentTimeMillis(), IpUtil.getClientIpFromRequest(request, requestHeader)); + System.currentTimeMillis(), + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); } return new ResponseEntity<>( @@ -195,7 +186,7 @@ public class RootController implements EnvironmentAware { ResponseEntity result; final Target target = controllerManagement.updateLastTargetQuery(targetid, - IpUtil.getClientIpFromRequest(request, requestHeader)); + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); if (checkModule(fileName, module)) { @@ -265,7 +256,8 @@ public class RootController implements EnvironmentAware { public ResponseEntity downloadArtifactMd5(@PathVariable final String targetid, @PathVariable final Long softwareModuleId, @PathVariable final String fileName, 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); @@ -311,7 +303,7 @@ public class RootController implements EnvironmentAware { LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource); final Target target = controllerManagement.updateLastTargetQuery(targetid, - IpUtil.getClientIpFromRequest(request, requestHeader)); + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); final Action action = findActionWithExceptionIfNotFound(actionId); if (!action.getTarget().getId().equals(target.getId())) { @@ -362,7 +354,7 @@ public class RootController implements EnvironmentAware { LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback); final Target target = controllerManagement.updateLastTargetQuery(targetid, - IpUtil.getClientIpFromRequest(request, requestHeader)); + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); if (!actionId.equals(feedback.getId())) { LOG.warn( @@ -467,7 +459,8 @@ public class RootController implements EnvironmentAware { + ControllerConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity putConfigData(@Valid @RequestBody final ConfigData configData, @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()); @@ -493,7 +486,7 @@ public class RootController implements EnvironmentAware { LOG.debug("getControllerCancelAction({})", targetid); final Target target = controllerManagement.updateLastTargetQuery(targetid, - IpUtil.getClientIpFromRequest(request, requestHeader)); + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); final Action action = findActionWithExceptionIfNotFound(actionId); if (!action.getTarget().getId().equals(target.getId())) { @@ -540,7 +533,7 @@ public class RootController implements EnvironmentAware { LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback); final Target target = controllerManagement.updateLastTargetQuery(targetid, - IpUtil.getClientIpFromRequest(request, requestHeader)); + IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); if (!actionId.equals(feedback.getId())) { LOG.warn( diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/PagingUtility.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/PagingUtility.java index 1503b8bda..4fb854608 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/PagingUtility.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/PagingUtility.java @@ -23,11 +23,6 @@ import org.springframework.data.domain.Sort.Direction; /** * Utility class for for paged body generation. * - * - * - * - * - * */ public final class PagingUtility { /* @@ -90,8 +85,9 @@ public final class PagingUtility { if (sortParam != null) { sorting = new Sort(SortUtility.parse(ActionFields.class, sortParam)); } else { - // default sort - sorting = new Sort(Direction.ASC, ActionFields.ID.getFieldName()); + // default sort is DESC in case of action to match behavior + // of management UI (last entry on top) + sorting = new Sort(Direction.DESC, ActionFields.ID.getFieldName()); } return sorting; } @@ -101,8 +97,9 @@ public final class PagingUtility { if (sortParam != null) { sorting = new Sort(SortUtility.parse(ActionStatusFields.class, sortParam)); } else { - // default sort - sorting = new Sort(Direction.ASC, ActionStatusFields.ID.getFieldName()); + // default sort is DESC in case of action status to match behavior + // of management UI (last entry on top) + sorting = new Sort(Direction.DESC, ActionStatusFields.ID.getFieldName()); } return sorting; } diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetMapper.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetMapper.java index 4f6ddafb8..b6c6c1539 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetMapper.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetMapper.java @@ -297,8 +297,8 @@ final public class TargetMapper { final ActionStatusRest result = new ActionStatusRest(); result.setMessages(actionStatus.getMessages()); - result.setReportedAt(action.getCreatedAt()); - result.setStatusId(action.getId()); + result.setReportedAt(actionStatus.getCreatedAt()); + result.setStatusId(actionStatus.getId()); result.setType(getNameOfActionStatusType(actionStatus.getStatus())); return result; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java index 01f018327..69a38f87d 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java @@ -235,7 +235,7 @@ public class TargetResource implements TargetRestApi { final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final Sort sorting = PagingUtility.sanitizeActionStatusSortParam(sortParam); - final Page statusList = this.deploymentManagement.findActionStatusMessagesByActionInDescOrder( + final Page statusList = this.deploymentManagement.findActionStatusByAction( new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting), action, true); return new ResponseEntity<>( diff --git a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java index e9b0bd60a..a8ebfdd9d 100644 --- a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java +++ b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.rest.resource; import static org.fest.assertions.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; 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.exception.SpServerError; import org.eclipse.hawkbit.im.authentication.SpPermission; +import org.eclipse.hawkbit.repository.ActionFields; import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; 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"), 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 + "/" + RestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId() + "/status") .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(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].id", equalTo(status.getId().intValue()))) .andExpect(jsonPath("content.[0].type", equalTo("finished"))) .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"))); } @@ -839,7 +838,8 @@ public class TargetResourceTest extends AbstractIntegrationTest { final List actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId); 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()) .andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue()))) .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))); } + @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 = 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 = 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 = 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 public void getMultipleActionsWithPagingLimitRequestParameter() throws Exception { final String knownTargetId = "targetId"; @@ -864,7 +966,8 @@ public class TargetResourceTest extends AbstractIntegrationTest { // page 1: one entry mvc.perform(get( 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()) .andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue()))) .andExpect(jsonPath("content.[0].type", equalTo("cancel"))) @@ -879,7 +982,9 @@ public class TargetResourceTest extends AbstractIntegrationTest { mvc.perform(get( RestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + RestConstants.TARGET_V1_ACTIONS) .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()) .andExpect(jsonPath("content.[0].id", equalTo(actions.get(1).getId().intValue()))) .andExpect(jsonPath("content.[0].type", equalTo("update"))) @@ -907,7 +1012,8 @@ public class TargetResourceTest extends AbstractIntegrationTest { + "?offset=0&limit=50&sort=id:DESC"; } - private List generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId) { + private List generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId) + throws InterruptedException { final PageRequest pageRequest = new PageRequest(0, 100, Direction.ASC, ActionStatusFields.ID.getFieldName()); @@ -925,6 +1031,8 @@ public class TargetResourceTest extends AbstractIntegrationTest { final List updatedTargets = deploymentManagement.assignDistributionSet(one, targets) .getAssignedTargets(); // 2nd update + // sleep 10ms to ensure that we can sort by reportedAt + Thread.sleep(10); deploymentManagement.assignDistributionSet(two, updatedTargets); // two updates, one cancelation @@ -951,54 +1059,6 @@ public class TargetResourceTest extends AbstractIntegrationTest { equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1))))); } - @Test - public void getActionStatusWithMultipleResultsWithPagingLimitRequestParameter() throws Exception { - final int limitSize = 1; - final String knownTargetId = "targetId"; - final List 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 public void assignDistributionSetToTarget() throws Exception { diff --git a/hawkbit-rest-resource/src/test/resources/application-test.properties b/hawkbit-rest-resource/src/test/resources/application-test.properties index bdd959ca2..92506caa4 100644 --- a/hawkbit-rest-resource/src/test/resources/application-test.properties +++ b/hawkbit-rest-resource/src/test/resources/application-test.properties @@ -24,7 +24,7 @@ hawkbit.server.database=H2 hawkbit.server.database.env=TEST 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 diff --git a/hawkbit-rest-resource/src/test/resources/log4j2.xml b/hawkbit-rest-resource/src/test/resources/log4j2.xml index 26437af34..98ea99ac9 100644 --- a/hawkbit-rest-resource/src/test/resources/log4j2.xml +++ b/hawkbit-rest-resource/src/test/resources/log4j2.xml @@ -17,7 +17,7 @@ - + diff --git a/hawkbit-security-core/pom.xml b/hawkbit-security-core/pom.xml index 011acc95b..a3b262726 100644 --- a/hawkbit-security-core/pom.xml +++ b/hawkbit-security-core/pom.xml @@ -59,6 +59,11 @@ org.springframework.boot spring-boot + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/DdiSecurityProperties.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/DdiSecurityProperties.java new file mode 100644 index 000000000..ce1ff91d8 --- /dev/null +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/DdiSecurityProperties.java @@ -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 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 getTrustedIPs() { + return trustedIPs; + } + + /** + * @param trustedIPs + * the trustedIPs to set + */ + public void setTrustedIPs(final List 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; + } + } + + } + +} diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/HawkbitSecurityProperties.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/HawkbitSecurityProperties.java new file mode 100644 index 000000000..7b157da65 --- /dev/null +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/HawkbitSecurityProperties.java @@ -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; + } + + } + } +} diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java deleted file mode 100644 index 8cc056f15..000000000 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityProperties.java +++ /dev/null @@ -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 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 getTrustedIPs() { - return trustedIPs; - } - - /** - * @param trustedIPs - * the trustedIPs to set - */ - public void setTrustedIPs(final List trustedIPs) { - this.trustedIPs = trustedIPs; - } - - } - - /** - * Inner class for anonymous enable configuration. - */ - @Component - @ConfigurationProperties("hawkbit.server.controller.security.authentication.anonymous") - public static class AnoymousAuthenticationProperties { - private Boolean enabled = Boolean.FALSE; - - /** - * @param enabled - * the enabled to set - */ - public void setEnabled(final Boolean enabled) { - this.enabled = enabled; - } - - /** - * @return the enabled - */ - public Boolean getEnabled() { - return enabled; - } - - } - - @Autowired - private RpProperties rppProperties; - - @Autowired - private AnoymousAuthenticationProperties authenticationsProperties; - - public String getRpCnHeader() { - return rppProperties.getCnHeader(); - } - - public String getRpSslIssuerHashHeader() { - return rppProperties.getSslIssuerHashHeader(); - } - - public List getRpTrustedIPs() { - return rppProperties.getTrustedIPs(); - } - - public Boolean getAnonymousEnabled() { - return authenticationsProperties.getEnabled(); - } - -} diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/SPInfo.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/SPInfo.java index 3a2a696df..feb94c0d7 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/SPInfo.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/SPInfo.java @@ -11,90 +11,24 @@ package org.eclipse.hawkbit.util; import javax.servlet.MultipartConfigElement; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** * Bean which contains all informations about the SP software, e.g. like * version, built time etc. from the environment. * - * - * - * */ @Component -public class SPInfo implements EnvironmentAware { +public class SPInfo { // package private for testing purposes static final String UNKNOWN_VERSION = "unknown"; static final String UNKNOWN_CREDENTIAL = "unknown credential"; - private Environment environmentData; - @Autowired 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 * been configured. diff --git a/hawkbit-ui/pom.xml b/hawkbit-ui/pom.xml index 0bd083e36..c9bd54b01 100644 --- a/hawkbit-ui/pom.xml +++ b/hawkbit-ui/pom.xml @@ -213,7 +213,6 @@ org.vaadin.addons tokenfield - org.vaadin.alump.distributionbar dbar-addon @@ -222,7 +221,11 @@ org.vaadin.addons contextmenu - + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java new file mode 100644 index 000000000..b23935826 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java @@ -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; + } + } + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java index 9beeff56a..484333219 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java @@ -15,16 +15,14 @@ import javax.servlet.http.Cookie; import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator; 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.documentation.DocumentationPageLink; import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider; -import org.eclipse.hawkbit.util.SPInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; @@ -61,7 +59,7 @@ import com.vaadin.ui.themes.ValoTheme; */ @SpringView(name = "") @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 long serialVersionUID = 1L; private static final Logger LOGGER = LoggerFactory.getLogger(LoginView.class); @@ -76,7 +74,7 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware private I18N i18n; @Autowired - private transient SPInfo spInfo; + private transient UiProperties uiProperties; @Autowired private transient MultitenancyIndicator multiTenancyIndicator; @@ -86,9 +84,6 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware private PasswordField password; private Button signin; - private Boolean secureCookie = Boolean.TRUE; - private String userManagementLoginUrl; - void loginAuthenticationFailedNotification() { final Notification notification = new Notification(i18n.get("notification.login.failed.title")); 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 String lookForDemoFragment = spURI.toString(); 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(); @@ -243,18 +239,18 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware links.addComponent(demoLink); demoLink.addStyleName(ValoTheme.LINK_SMALL); - if (spInfo.getRequestAccountEmail() != null) { + if (!uiProperties.getLinks().getRequestAccount().isEmpty()) { final Link requestAccountLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_REQUESTACCOUNT, - i18n.get("link.requestaccount.name"), spInfo.getRequestAccountEmail(), FontAwesome.SHOPPING_CART, - "", linkStyle, true); + i18n.get("link.requestaccount.name"), uiProperties.getLinks().getRequestAccount(), + FontAwesome.SHOPPING_CART, "", linkStyle, true); links.addComponent(requestAccountLink); requestAccountLink.addStyleName(ValoTheme.LINK_SMALL); } - if (userManagementLoginUrl != null) { + if (!uiProperties.getLinks().getUserManagement().isEmpty()) { final Link userManagementLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_USERMANAGEMENT, - i18n.get("link.usermanagement.name"), userManagementLoginUrl, FontAwesome.USERS, "_blank", - linkStyle, true); + i18n.get("link.usermanagement.name"), uiProperties.getLinks().getUserManagement(), + FontAwesome.USERS, "_blank", linkStyle, true); links.addComponent(userManagementLink); userManagementLink.addStyleName(ValoTheme.LINK_SMALL); } @@ -315,7 +311,7 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware // 100 days tenantCookie.setMaxAge(3600 * 24 * 100); tenantCookie.setHttpOnly(true); - tenantCookie.setSecure(secureCookie); + tenantCookie.setSecure(uiProperties.getLogin().getCookie().isSecure()); VaadinService.getCurrentResponse().addCookie(tenantCookie); } @@ -324,7 +320,7 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware // 100 days usernameCookie.setMaxAge(3600 * 24 * 100); usernameCookie.setHttpOnly(true); - usernameCookie.setSecure(secureCookie); + usernameCookie.setSecure(uiProperties.getLogin().getCookie().isSecure()); VaadinService.getCurrentResponse().addCookie(usernameCookie); } @@ -368,16 +364,4 @@ public class LoginView extends VerticalLayout implements View, EnvironmentAware 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); - } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java index df9d8a60d..f8d53b1bf 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/actionhistory/ActionHistoryTable.java @@ -16,6 +16,7 @@ import java.util.StringJoiner; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; 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.PageRequest; 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.EventScope; 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 .findActionWithDetails(actionId); - final Pageable pageReq = new PageRequest(0, 1000); - final Page actionStatusList = deploymentManagement - .findActionStatusMessagesByActionInDescOrder(pageReq, action, - managementUIState.isActionHistoryMaximized()); + final Pageable pageReq = new PageRequest(0, 1000, + new Sort(Direction.DESC, ActionStatusFields.ID.getFieldName())); + final Page actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, action, + managementUIState.isActionHistoryMaximized()); final List content = actionStatusList.getContent(); /* * Since the recent action status and messages are already diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java index 44ec9ea40..beff0e6b9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java @@ -18,17 +18,16 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import org.eclipse.hawkbit.HawkbitServerProperties; import org.eclipse.hawkbit.im.authentication.PermissionService; 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.documentation.DocumentationPageLink; import org.eclipse.hawkbit.ui.menu.DashboardEvent.PostViewChangeEvent; import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider; -import org.eclipse.hawkbit.util.SPInfo; 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.userdetails.UserDetails; 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.ui.Alignment; import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Component; import com.vaadin.ui.CustomComponent; @@ -50,7 +48,6 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.MenuBar; -import com.vaadin.ui.MenuBar.Command; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; @@ -61,11 +58,17 @@ import com.vaadin.ui.themes.ValoTheme; */ @SpringComponent @UIScope -public final class DashboardMenu extends CustomComponent implements EnvironmentAware { +public final class DashboardMenu extends CustomComponent { @Autowired private I18N i18n; + @Autowired + private transient UiProperties uiProperties; + + @Autowired + private transient HawkbitServerProperties serverProperties; + private static final long serialVersionUID = 5394474618559481462L; 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"; // this should be resolved when we introduce event bus on UI to just inform - // the buttons directly - // via events + // the buttons directly via events private final List menuButtons = new ArrayList<>(); @Autowired private transient PermissionService permissionService; - @Autowired - private transient SPInfo spInfo; - @Autowired private final List dashboardVaadinViews = new ArrayList<>(); @@ -91,12 +90,10 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA private boolean accessibleViewsEmpty; - private String userManagementLoginUrl; - /** * initializing the view and creating the layout, cannot be done in the - * custructor because the constructor will be called by spring and the - * dashabord must be initialized when the dashboard UI is creating. + * constructor because the constructor will be called by spring and the + * dashboard must be initialized when the dashboard UI is creating. */ public void init() { initialViewName = ""; @@ -162,20 +159,20 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA links.addComponent(docuLink); links.setComponentAlignment(docuLink, Alignment.BOTTOM_CENTER); - if (userManagementLoginUrl != null) { + if (!uiProperties.getLinks().getUserManagement().isEmpty()) { final Link userManagementLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_USERMANAGEMENT, - i18n.get("link.usermanagement.name"), userManagementLoginUrl, FontAwesome.USERS, "_blank", - linkStyle, true); + i18n.get("link.usermanagement.name"), uiProperties.getLinks().getUserManagement(), + FontAwesome.USERS, "_blank", linkStyle, true); userManagementLink.setDescription(i18n.get("link.usermanagement.name")); links.addComponent(userManagementLink); userManagementLink.setSizeFull(); links.setComponentAlignment(userManagementLink, Alignment.BOTTOM_CENTER); } - if (spInfo.getSupportEmail() != null) { + if (!uiProperties.getLinks().getSupport().isEmpty()) { final Link supportLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_SUPPORT, - i18n.get("link.support.name"), spInfo.getSupportEmail(), FontAwesome.ENVELOPE_O, "", linkStyle, - true); + i18n.get("link.support.name"), uiProperties.getLinks().getSupport(), FontAwesome.ENVELOPE_O, "", + linkStyle, true); supportLink.setDescription(i18n.get("link.support.name")); supportLink.setSizeFull(); links.addComponent(supportLink); @@ -222,12 +219,7 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA settingsItem.setDescription(user.getUsername()); } - settingsItem.addItem("Sign Out", new Command() { - @Override - public void menuSelected(final MenuItem selectedItem) { - Page.getCurrent().setLocation("/UI/logout"); - } - }); + settingsItem.addItem("Sign Out", selectedItem -> Page.getCurrent().setLocation("/UI/logout")); return settings; } @@ -254,14 +246,11 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA } private Component buildToggleButton() { - final Button valoMenuToggleButton = new Button("Menu", new ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - if (getCompositionRoot().getStyleName().contains(STYLE_VISIBLE)) { - getCompositionRoot().removeStyleName(STYLE_VISIBLE); - } else { - getCompositionRoot().addStyleName(STYLE_VISIBLE); - } + final Button valoMenuToggleButton = new Button("Menu", (ClickListener) event -> { + if (getCompositionRoot().getStyleName().contains(STYLE_VISIBLE)) { + getCompositionRoot().removeStyleName(STYLE_VISIBLE); + } else { + getCompositionRoot().addStyleName(STYLE_VISIBLE); } }); valoMenuToggleButton.setIcon(FontAwesome.LIST); @@ -307,7 +296,7 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA final Label label = new Label(); label.setSizeFull(); label.setStyleName("version-layout"); - label.setValue(spInfo.getVersion()); + label.setValue(serverProperties.getBuild().getVersion()); return label; } @@ -341,11 +330,6 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA 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. * @@ -411,12 +395,7 @@ public final class DashboardMenu extends CustomComponent implements EnvironmentA setDescription(view.getDashboardCaptionLong()); /* Avoid double click */ setDisableOnClick(true); - addClickListener(new ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - event.getComponent().getUI().getNavigator().navigateTo(view.getViewName()); - } - }); + addClickListener(event -> event.getComponent().getUI().getNavigator().navigateTo(view.getViewName())); } diff --git a/pom.xml b/pom.xml index 577e39515..3d70d980c 100644 --- a/pom.xml +++ b/pom.xml @@ -559,7 +559,6 @@ org.json json ${json.version} - test de.flapdoodle.embed