Merge remote-tracking branch 'origin/master' into

fix_Introduce_consitent_button_icon_for_update_create_dialogs

Conflicts:
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetAddUpdateWindowLayout.java


Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>
This commit is contained in:
Melanie Retter
2016-06-20 15:45:24 +02:00
107 changed files with 1373 additions and 570 deletions

View File

@@ -8,6 +8,7 @@
*/ */
package org.eclipse.hawkbit.simulator.amqp; package org.eclipse.hawkbit.simulator.amqp;
import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -15,6 +16,7 @@ import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@@ -65,9 +67,12 @@ public class AmqpConfiguration {
* @return the queue * @return the queue
*/ */
@Bean @Bean
public Queue receiverConnectorQueueFromSp() { public Queue receiverConnectorQueueFromHawkBit() {
return new Queue(amqpProperties.getReceiverConnectorQueueFromSp(), true, false, false, final Map<String, Object> arguments = getDeadLetterExchangeArgs();
getDeadLetterExchangeArgs()); arguments.putAll(getTTLMaxArgs());
return QueueBuilder.nonDurable(amqpProperties.getReceiverConnectorQueueFromSp()).withArguments(arguments)
.build();
} }
/** /**
@@ -89,7 +94,7 @@ public class AmqpConfiguration {
*/ */
@Bean @Bean
public Binding bindReceiverQueueToSpExchange() { public Binding bindReceiverQueueToSpExchange() {
return BindingBuilder.bind(receiverConnectorQueueFromSp()).to(exchangeQueueToConnector()); return BindingBuilder.bind(receiverConnectorQueueFromHawkBit()).to(exchangeQueueToConnector());
} }
/** /**
@@ -99,7 +104,7 @@ public class AmqpConfiguration {
*/ */
@Bean @Bean
public Queue deadLetterQueue() { public Queue deadLetterQueue() {
return new Queue(amqpProperties.getDeadLetterQueue(), true, false, true); return QueueBuilder.nonDurable(amqpProperties.getDeadLetterQueue()).withArguments(getTTLMaxArgs()).build();
} }
/** /**
@@ -145,4 +150,11 @@ public class AmqpConfiguration {
return args; return args;
} }
private static Map<String, Object> getTTLMaxArgs() {
final Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", Duration.ofDays(1).toMillis());
args.put("x-max-length", 100_000);
return args;
}
} }

View File

@@ -82,21 +82,4 @@ public class MessageService {
clazz.getTypeName()); clazz.getTypeName());
return (T) rabbitTemplate.getMessageConverter().fromMessage(message); return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
} }
/**
* Method to verify if lwm2m header is set.
*
* @param message
* the message with the header
* @param header
* the header to verify
*/
public void checkIfLwm2mHeaderEmpty(final Message message, final String header) {
final Object headerObject = message.getMessageProperties().getHeaders().get(header);
if (null == headerObject) {
logAndThrowMessageError(message, "Header of " + header + "empty.");
}
}
} }

View File

@@ -19,7 +19,6 @@
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" /> <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" /> <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
<Logger name="org.apache.tomcat.jdbc.pool.ConnectionPool" level="DEBUG" />
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" /> <Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
<!-- Security Log with hints on potential attacks --> <!-- Security Log with hints on potential attacks -->

View File

@@ -23,7 +23,6 @@ import org.springframework.scheduling.annotation.EnableAsync;
* *
* *
*/ */
@Configuration @Configuration
@EnableAsync @EnableAsync
@ConditionalOnMissingBean(AsyncConfigurer.class) @ConditionalOnMissingBean(AsyncConfigurer.class)

View File

@@ -20,7 +20,7 @@ public class AsyncConfigurerThreadpoolProperties {
/** /**
* Max queue size for central event executor. * Max queue size for central event executor.
*/ */
private Integer queuesize = 250; private Integer queuesize = 5_000;
/** /**
* Core processing threads for central event executor. * Core processing threads for central event executor.
@@ -30,7 +30,7 @@ public class AsyncConfigurerThreadpoolProperties {
/** /**
* Maximum thread pool size for central event executor. * Maximum thread pool size for central event executor.
*/ */
private Integer maxthreads = 50; private Integer maxthreads = 20;
/** /**
* When the number of threads is greater than the core, this is the maximum * When the number of threads is greater than the core, this is the maximum

View File

@@ -11,7 +11,9 @@ package org.eclipse.hawkbit.autoconfigure.scheduling;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -21,6 +23,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor; import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -39,21 +44,38 @@ public class ExecutorAutoConfiguration {
private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties; private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties;
/** /**
* @return ExecutorService for general purpose multi threaded operations * @return ExecutorService with security context availability in thread
* execution..
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public Executor asyncExecutor() { public Executor asyncExecutor() {
return new DelegatingSecurityContextExecutor(threadPoolExecutor());
}
/**
* @return central ThreadPoolExecutor for general purpose multi threaded
* operations. Tries an orderly shutdown when destroyed.
*/
@Bean(destroyMethod = "shutdown")
public ThreadPoolExecutor threadPoolExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>( final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(
asyncConfigurerProperties.getQueuesize()); asyncConfigurerProperties.getQueuesize());
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(), return new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(),
asyncConfigurerProperties.getMaxthreads(), asyncConfigurerProperties.getIdletimeout(), asyncConfigurerProperties.getMaxthreads(), asyncConfigurerProperties.getIdletimeout(),
TimeUnit.MILLISECONDS, blockingQueue, TimeUnit.MILLISECONDS, blockingQueue,
new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build()); new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build(),
threadPoolExecutor.setRejectedExecutionHandler((r, executor) -> LOGGER.warn( new PoolSizeExceededPolicy());
"Reject runnable for centralExecutorService, reached limit of queue size {}", }
executor.getQueue().size()));
return new DelegatingSecurityContextExecutor(threadPoolExecutor); private static class PoolSizeExceededPolicy extends CallerRunsPolicy {
@Override
public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
LOGGER.warn(
"Caller has to run on its own instead of centralExecutorService, reached limit of queue size {}",
executor.getQueue().size());
super.rejectedExecution(r, executor);
}
} }
/** /**
@@ -69,4 +91,32 @@ public class ExecutorAutoConfiguration {
return new DelegatingSecurityContextExecutor(threadPoolExecutor); return new DelegatingSecurityContextExecutor(threadPoolExecutor);
} }
/**
* @return {@link TaskExecutor} for task execution
*/
@Bean
@ConditionalOnMissingBean
public TaskExecutor taskExecutor() {
return new ConcurrentTaskExecutor(asyncExecutor());
}
/**
* @return {@link ScheduledExecutorService} based on
* {@link #threadPoolTaskScheduler()}.
*/
@Bean
@ConditionalOnMissingBean
public ScheduledExecutorService scheduledExecutorService() {
return threadPoolTaskScheduler().getScheduledExecutor();
}
/**
* @return {@link ThreadPoolTaskScheduler} for scheduled operations.
*/
@Bean
@ConditionalOnMissingBean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
} }

View File

@@ -0,0 +1,76 @@
/**
* 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.security;
import java.util.ArrayList;
import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator;
import org.eclipse.hawkbit.im.authentication.PermissionUtils;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/**
* Auto-configuration for the in-memory-user-management.
*
*/
@Configuration
@ConditionalOnMissingBean(UserDetailsService.class)
public class InMemoryUserManagementConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
final DaoAuthenticationProvider userDaoAuthenticationProvider = new TenantDaoAuthenticationProvider();
userDaoAuthenticationProvider.setUserDetailsService(userDetailsService());
auth.authenticationProvider(userDaoAuthenticationProvider);
}
/**
* @return the user details service to load a user from memory user manager.
*/
@Bean
@ConditionalOnMissingBean
public UserDetailsService userDetailsService() {
final InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(new ArrayList<>());
inMemoryUserDetailsManager.setAuthenticationManager(null);
inMemoryUserDetailsManager.createUser(new User("admin", "admin", PermissionUtils.createAllAuthorityList()));
return inMemoryUserDetailsManager;
}
/**
* @return the multi-tenancy indicator to disallow multi-tenancy
*/
@Bean
@ConditionalOnMissingBean
public MultitenancyIndicator multiTenancyIndicator() {
return () -> false;
}
private static class TenantDaoAuthenticationProvider extends DaoAuthenticationProvider {
@Override
protected Authentication createSuccessAuthentication(final Object principal,
final Authentication authentication, final UserDetails user) {
final UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
authentication.getCredentials(), user.getAuthorities());
result.setDetails(new TenantAwareAuthenticationDetails("DEFAULT", false));
return result;
}
}
}

View File

@@ -8,43 +8,17 @@
*/ */
package org.eclipse.hawkbit.autoconfigure.security; package org.eclipse.hawkbit.autoconfigure.security;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator;
import org.eclipse.hawkbit.im.authentication.PermissionService; import org.eclipse.hawkbit.im.authentication.PermissionService;
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.DdiSecurityProperties; import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware; import org.springframework.data.domain.AuditorAware;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for security. * {@link EnableAutoConfiguration Auto-configuration} for security.
@@ -88,119 +62,4 @@ public class SecurityAutoConfiguration {
return new SpringSecurityAuditorAware(); return new SpringSecurityAuditorAware();
} }
/**
* Auto-configuration for the in-memory-user-management.
*
*
*
*/
@Configuration
@ConditionalOnMissingBean(value = { UserAuthenticationFilter.class })
public static class InMemoryUserManagementConfiguration extends GlobalAuthenticationConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryUserManagementConfiguration.class);
@Autowired
private AuthenticationConfiguration configuration;
/*
* (non-Javadoc)
*
* @see org.springframework.security.config.annotation.authentication.
* configurers. GlobalAuthenticationConfigurerAdapter
* #configure(org.springframework.security.config.annotation.
* authentication.builders.AuthenticationManagerBuilder)
*/
@Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
final DaoAuthenticationProvider userDaoAuthenticationProvider = new TenantDaoAuthenticationProvider();
userDaoAuthenticationProvider.setUserDetailsService(userDetailsService());
auth.authenticationProvider(userDaoAuthenticationProvider);
}
/**
* @return the user details service to load a user from memory user
* manager.
*/
@Bean
public UserDetailsService userDetailsService() {
final InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(
new ArrayList<>());
inMemoryUserDetailsManager.setAuthenticationManager(null);
inMemoryUserDetailsManager.createUser(new User("admin", "admin", getAllAuthorities()));
return inMemoryUserDetailsManager;
}
/**
* @return the multi-tenancy indicator to disallow multi-tenancy
*/
@Bean
public MultitenancyIndicator multiTenancyIndicator() {
return new MultitenancyIndicator() {
@Override
public boolean isMultiTenancySupported() {
return false;
}
};
}
private Collection<SimpleGrantedAuthority> getAllAuthorities() {
final List<SimpleGrantedAuthority> allPermissions = new ArrayList<>();
final Field[] declaredFields = SpPermission.class.getDeclaredFields();
for (final Field field : declaredFields) {
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
try {
final String permissionName = (String) field.get(null);
allPermissions.add(new SimpleGrantedAuthority(permissionName));
} catch (final IllegalAccessException e) {
LOGGER.error(e.getMessage(), e);
}
}
}
return allPermissions;
}
private static class TenantDaoAuthenticationProvider extends DaoAuthenticationProvider {
/*
* (non-Javadoc)
*
* @see org.springframework.security.authentication.dao.
* AbstractUserDetailsAuthenticationProvider
* #createSuccessAuthentication(java.lang.Object,
* org.springframework.security.core.Authentication,
* org.springframework.security.core.userdetails.UserDetails)
*/
@Override
protected Authentication createSuccessAuthentication(final Object principal,
final Authentication authentication, final UserDetails user) {
final UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
authentication.getCredentials(), user.getAuthorities());
result.setDetails(new TenantAwareAuthenticationDetails("DEFAULT", false));
return result;
}
}
/**
* @return the {@link UserAuthenticationFilter} to include into the SP
* security configuration.
* @throws Exception
* lazy bean exception maybe if the authentication manager
* cannot be instantiated
*/
@Bean
public UserAuthenticationFilter userAuthenticationFilter() throws Exception {
return new UserAuthenticationFilterBasicAuth(configuration.getAuthenticationManager());
}
private static final class UserAuthenticationFilterBasicAuth extends BasicAuthenticationFilter
implements UserAuthenticationFilter {
private UserAuthenticationFilterBasicAuth(final AuthenticationManager authenticationManager) {
super(authenticationManager);
}
}
}
} }

View File

@@ -8,6 +8,10 @@
*/ */
package org.eclipse.hawkbit.autoconfigure.security; package org.eclipse.hawkbit.autoconfigure.security;
import static com.google.common.collect.Lists.newArrayList;
import static org.springframework.context.annotation.AdviceMode.ASPECTJ;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
@@ -46,18 +50,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer; import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -72,6 +77,8 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter; import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy; import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter; import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode; import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
@@ -83,30 +90,49 @@ import org.vaadin.spring.security.web.VaadinRedirectStrategy;
import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler; import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler; import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler;
import com.google.common.collect.Lists;
/** /**
* All configurations related to SP authentication and authorization layer. * All configurations related to HawkBit's authentication and authorization
* * layer.
*
*
*/ */
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true, mode = ASPECTJ, proxyTargetClass = true, securedEnabled = true)
@EnableWebMvcSecurity @EnableWebMvcSecurity
@Order(value = Ordered.HIGHEST_PRECEDENCE) @Order(value = HIGHEST_PRECEDENCE)
public class SecurityManagedConfiguration { public class SecurityManagedConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class); private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class);
@Autowired @Autowired
private HawkbitSecurityProperties securityProperties; private HawkbitSecurityProperties securityProperties;
@Autowired
private AuthenticationConfiguration configuration;
/**
* @return the {@link UserAuthenticationFilter} to include into the SP
* security configuration.
* @throws Exception
* lazy bean exception maybe if the authentication manager
* cannot be instantiated
*/
@Bean
@ConditionalOnMissingBean
public UserAuthenticationFilter userAuthenticationFilter() throws Exception {
return new UserAuthenticationFilterBasicAuth(configuration.getAuthenticationManager());
}
private static final class UserAuthenticationFilterBasicAuth extends BasicAuthenticationFilter
implements UserAuthenticationFilter {
private UserAuthenticationFilterBasicAuth(final AuthenticationManager authenticationManager) {
super(authenticationManager);
}
}
/** /**
* {@link WebSecurityConfigurer} for the internal SP controller API. * {@link WebSecurityConfigurer} for the internal SP controller API.
*
*
*
*/ */
@Configuration @Configuration
@Order(300) @Order(300)
@@ -114,19 +140,25 @@ public class SecurityManagedConfiguration {
@Autowired @Autowired
private ControllerManagement controllerManagement; private ControllerManagement controllerManagement;
@Autowired @Autowired
private TenantConfigurationManagement tenantConfigurationManagement; private TenantConfigurationManagement tenantConfigurationManagement;
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired @Autowired
private DdiSecurityProperties ddiSecurityConfiguration; private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Autowired @Autowired
private SystemSecurityContext systemSecurityContext; private SystemSecurityContext systemSecurityContext;
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource(); final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter( final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
@@ -164,16 +196,19 @@ public class SecurityManagedConfiguration {
} }
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) { if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info( LOG.info(
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************"); "******************\n** Anonymous controller security enabled, should only be used for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter( final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous", "controllerAnonymousFilter", "anonymous",
Lists.newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS), newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS),
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE))); new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)));
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource); anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and() httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and()
.securityContext().disable().anonymous().authenticationFilter(anoymousFilter); .securityContext().disable().anonymous().authenticationFilter(anoymousFilter);
} else { } else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter) httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter) .addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
.antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest() .antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest()
@@ -186,6 +221,7 @@ public class SecurityManagedConfiguration {
@Override @Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception { protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider( auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs())); ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
@@ -200,6 +236,7 @@ public class SecurityManagedConfiguration {
@Bean @Bean
@Order(50) @Order(50)
public FilterRegistrationBean dosFilter() { public FilterRegistrationBean dosFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(), filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(),
@@ -207,6 +244,7 @@ public class SecurityManagedConfiguration {
securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(), securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(),
securityProperties.getClients().getRemoteIpHeader())); securityProperties.getClients().getRemoteIpHeader()));
filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*"); filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*");
return filterRegBean; return filterRegBean;
} }
@@ -219,22 +257,21 @@ public class SecurityManagedConfiguration {
@Bean @Bean
@Order(100) @Order(100)
public FilterRegistrationBean eTagFilter() { public FilterRegistrationBean eTagFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
// eclude the URLs for downloading artifacts, so no eTag is generated in // Exclude the URLs for downloading artifacts, so no eTag is generated
// the // in the ShallowEtagHeaderFilter, just using the SH1 hash of the
// ShallowEtagHeaderFilter, just using the SH1 hash of the artifact // artifact itself as 'ETag', because otherwise the file will be copied
// itself as 'ETag', because // in memory!
// otherwise the file will be copied in memory!
filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter( filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter(
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/{tenant}/controller/artifacts/**", "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/{tenant}/controller/artifacts/**",
"/{targetid}/softwaremodules/{softwareModuleId}/artifacts/**")); "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/**"));
return filterRegBean; return filterRegBean;
} }
/** /**
* Security configuration for the REST management API of the health url. * Security configuration for the REST management API of the health url.
*
*
*/ */
@Configuration @Configuration
@Order(310) @Order(310)
@@ -249,28 +286,34 @@ public class SecurityManagedConfiguration {
/** /**
* Security configuration for the REST management API. * Security configuration for the REST management API.
*
*
*/ */
@Configuration @Configuration
@Order(350) @Order(350)
public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired @Autowired
private UserAuthenticationFilter userAuthenticationFilter; private UserAuthenticationFilter userAuthenticationFilter;
@Autowired @Autowired
private SystemManagement systemManagement; private SystemManagement systemManagement;
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private SecurityProperties springSecurityProperties;
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthEntryPoint.setRealmName(springSecurityProperties.getBasic().getRealm());
HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system.*").csrf().disable(); HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system.*").csrf().disable();
if (springSecurityProperties.isRequireSsl()) { if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} }
httpSec.addFilterBefore(new Filter() { httpSec.addFilterBefore(new Filter() {
@Override @Override
public void init(final FilterConfig filterConfig) throws ServletException { public void init(final FilterConfig filterConfig) throws ServletException {
@@ -296,12 +339,13 @@ public class SecurityManagedConfiguration {
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN) .hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/**") .antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/**")
.hasAnyAuthority(SpPermission.SYSTEM_DIAG); .hasAnyAuthority(SpPermission.SYSTEM_DIAG);
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint);
} }
} }
/** /**
* {@link WebSecurityConfigurer} for external (management) access. * {@link WebSecurityConfigurer} for external (management) access.
*
*/ */
@Configuration @Configuration
@Order(400) @Order(400)
@@ -311,10 +355,13 @@ public class SecurityManagedConfiguration {
private static final String XFRAME_OPTION_DENY = "DENY"; private static final String XFRAME_OPTION_DENY = "DENY";
private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN"; private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN";
private static final String XFAME_OPTION_ALLOW_FROM = "ALLOW-FROM"; private static final String XFAME_OPTION_ALLOW_FROM = "ALLOW-FROM";
@Autowired @Autowired
private VaadinSecurityContext vaadinSecurityContext; private VaadinSecurityContext vaadinSecurityContext;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Autowired @Autowired
private HawkbitSecurityProperties securityProperties; private HawkbitSecurityProperties securityProperties;
@@ -346,10 +393,13 @@ public class SecurityManagedConfiguration {
*/ */
@Bean @Bean
public VaadinAuthenticationSuccessHandler redirectSaveHandler() { public VaadinAuthenticationSuccessHandler redirectSaveHandler() {
final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(); final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler();
handler.setRedirectStrategy(vaadinRedirectStrategy()); handler.setRedirectStrategy(vaadinRedirectStrategy());
handler.setDefaultTargetUrl("/UI/"); handler.setDefaultTargetUrl("/UI/");
handler.setTargetUrlParameter("r"); handler.setTargetUrlParameter("r");
return handler; return handler;
} }
@@ -371,7 +421,8 @@ public class SecurityManagedConfiguration {
// configuration xframe-option // configuration xframe-option
final String confXframeOption = securityProperties.getXframe().getOption(); final String confXframeOption = securityProperties.getXframe().getOption();
final String confAllowFromUri = securityProperties.getXframe().getAllowfrom(); final String confAllowFromUri = securityProperties.getXframe().getAllowfrom();
if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri.isEmpty()) {
if (XFAME_OPTION_ALLOW_FROM.equals(confXframeOption) && confAllowFromUri.isEmpty()) {
// if allow-from option is specified but no allowFromUri throw // if allow-from option is specified but no allowFromUri throw
// exception // exception
throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM" throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM"
@@ -380,9 +431,8 @@ public class SecurityManagedConfiguration {
} }
// workaround regex: we need to exclude the URL /UI/HEARTBEAT here // workaround regex: we need to exclude the URL /UI/HEARTBEAT here
// because we bound the // because we bound the vaadin application to /UI and not to root,
// vaadin application to /UI and not to root, described in // described in vaadin-forum:
// vaadin-forum:
// https://vaadin.com/forum#!/thread/3200565. // https://vaadin.com/forum#!/thread/3200565.
HttpSecurity httpSec = http.regexMatcher("(?!.*HEARTBEAT)^.*\\/UI.*$") HttpSecurity httpSec = http.regexMatcher("(?!.*HEARTBEAT)^.*\\/UI.*$")
// disable as CSRF is handled by Vaadin // disable as CSRF is handled by Vaadin
@@ -391,8 +441,9 @@ public class SecurityManagedConfiguration {
if (springSecurityProperties.isRequireSsl()) { if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} else { } else {
LOG.info( LOG.info(
"\"******************\\n** Requires HTTPS Security has been disabled for UI, should only use for developing purposes **\\n******************\""); "\"******************\\n** Requires HTTPS Security has been disabled for UI, should only be used for developing purposes **\\n******************\"");
} }
// for UI integrator we allow frame integration on same origin // for UI integrator we allow frame integration on same origin
@@ -441,9 +492,6 @@ public class SecurityManagedConfiguration {
/** /**
* A Websecruity config to handle and filter the download ids. * A Websecruity config to handle and filter the download ids.
*
*
*
*/ */
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@@ -459,6 +507,7 @@ public class SecurityManagedConfiguration {
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter( final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter(
downloadIdCache); downloadIdCache);
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager()); downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager());
@@ -476,32 +525,21 @@ public class SecurityManagedConfiguration {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider( auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs())); ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }
} }
/** /**
* After a successful login on the UI we need to ensure to create the tenant * After a successful login on the UI we need to ensure to create the tenant
* meta data within SP. * meta data within SP.
*
*
*/ */
class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler { class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler {
@Autowired @Autowired
private SystemManagement systemManagement; private SystemManagement systemManagement;
/*
* (non-Javadoc)
*
* @see org.vaadin.spring.security.web.authentication.
* SavedRequestAwareVaadinAuthenticationSuccessHandler
* #onAuthenticationSuccess(org.springframework.security.core.
* Authentication)
*/
@Override @Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception { public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) { if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) {
systemManagement systemManagement
.getTenantMetadata(((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString()); .getTenantMetadata(((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString());
@@ -515,14 +553,13 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
// has been fixed. // has been fixed.
systemManagement.getTenantMetadata("DEFAULT"); systemManagement.getTenantMetadata("DEFAULT");
} }
super.onAuthenticationSuccess(authentication); super.onAuthenticationSuccess(authentication);
} }
} }
/** /**
* Sevletfilter to create metadata after successful authentication over RESTful. * Sevletfilter to create metadata after successful authentication over RESTful.
*
*
*/ */
class AuthenticationSuccessTenantMetadataCreationFilter implements Filter { class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
@@ -543,11 +580,13 @@ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
@Override @Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
final String currentTenant = tenantAware.getCurrentTenant(); final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant != null) { if (currentTenant != null) {
// lazy initialize tenant meta data after successful authentication // lazy initialize tenant meta data after successful authentication
systemManagement.getTenantMetadata(currentTenant); systemManagement.getTenantMetadata(currentTenant);
} }
chain.doFilter(request, response); chain.doFilter(request, response);
} }

View File

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

View File

@@ -7,6 +7,9 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# Displayed basic auth realm
security.basic.realm=HawkBit
# JPA / Datasource # JPA / Datasource
spring.jpa.eclipselink.eclipselink.weaving=false spring.jpa.eclipselink.eclipselink.weaving=false
spring.jpa.database=H2 spring.jpa.database=H2
@@ -27,12 +30,6 @@ vaadin.servlet.urlMapping=/UI/*
vaadin.servlet.heartbeatInterval=60 vaadin.servlet.heartbeatInterval=60
vaadin.servlet.closeIdleSessions=false vaadin.servlet.closeIdleSessions=false
# Defines the thread pool executor
hawkbit.threadpool.corethreads=5
hawkbit.threadpool.maxthreads=20
hawkbit.threadpool.idletimeout=10000
hawkbit.threadpool.queuesize=20000
# Defines the polling time for the controllers in HH:MM:SS notation # Defines the polling time for the controllers in HH:MM:SS notation
hawkbit.controller.pollingTime=00:05:00 hawkbit.controller.pollingTime=00:05:00
hawkbit.controller.pollingOverdueTime=00:05:00 hawkbit.controller.pollingOverdueTime=00:05:00

View File

@@ -134,7 +134,7 @@ public class DdiArtifactStoreController implements DdiDlArtifactStoreControllerR
private Action checkAndReportDownloadByTarget(final HttpServletRequest request, final String targetid, private Action checkAndReportDownloadByTarget(final HttpServletRequest request, final String targetid,
final LocalArtifact artifact) { final LocalArtifact artifact) {
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader())); IpUtil.getClientIpFromRequest(request, securityProperties));
final Action action = controllerManagement final Action action = controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), artifact.getSoftwareModule()); .getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), artifact.getSoftwareModule());

View File

@@ -119,16 +119,14 @@ public class DdiRootController implements DdiRootControllerRestApi {
public ResponseEntity<DdiControllerBase> getControllerBase(@PathVariable("targetid") final String targetid) { public ResponseEntity<DdiControllerBase> getControllerBase(@PathVariable("targetid") final String targetid) {
LOG.debug("getControllerBase({})", targetid); LOG.debug("getControllerBase({})", targetid);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) { if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) {
LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid); LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid);
controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED, controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED,
System.currentTimeMillis(), System.currentTimeMillis(), IpUtil.getClientIpFromRequest(
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
} }
return new ResponseEntity<>( return new ResponseEntity<>(
@@ -143,9 +141,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("fileName") final String fileName) { @PathVariable("fileName") final String fileName) {
ResponseEntity<InputStream> result; ResponseEntity<InputStream> result;
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
if (checkModule(fileName, module)) { if (checkModule(fileName, module)) {
@@ -200,9 +197,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
public ResponseEntity<Void> downloadArtifactMd5(@PathVariable("targetid") final String targetid, public ResponseEntity<Void> downloadArtifactMd5(@PathVariable("targetid") final String targetid,
@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName) { @PathVariable("fileName") final String fileName) {
controllerManagement.updateLastTargetQuery(targetid, controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
@@ -228,9 +224,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource) { @RequestParam(value = "c", required = false, defaultValue = "-1") final int resource) {
LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource); LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
final Action action = findActionWithExceptionIfNotFound(actionId); final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) { if (!action.getTarget().getId().equals(target.getId())) {
@@ -263,9 +258,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("targetid") final String targetid, @PathVariable("actionId") @NotEmpty final Long actionId) { @PathVariable("targetid") final String targetid, @PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback); LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
if (!actionId.equals(feedback.getId())) { if (!actionId.equals(feedback.getId())) {
LOG.warn( LOG.warn(
@@ -357,9 +351,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@Override @Override
public ResponseEntity<Void> putConfigData(@Valid @RequestBody final DdiConfigData configData, public ResponseEntity<Void> putConfigData(@Valid @RequestBody final DdiConfigData configData,
@PathVariable("targetid") final String targetid) { @PathVariable("targetid") final String targetid) {
controllerManagement.updateLastTargetQuery(targetid, controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
controllerManagement.updateControllerAttributes(targetid, configData.getData()); controllerManagement.updateControllerAttributes(targetid, configData.getData());
@@ -372,9 +365,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("actionId") @NotEmpty final Long actionId) { @PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("getControllerCancelAction({})", targetid); LOG.debug("getControllerCancelAction({})", targetid);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
final Action action = findActionWithExceptionIfNotFound(actionId); final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) { if (!action.getTarget().getId().equals(target.getId())) {
@@ -403,9 +395,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("actionId") @NotEmpty final Long actionId) { @PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback); LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback);
final Target target = controllerManagement.updateLastTargetQuery(targetid, final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
IpUtil.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
securityProperties.getClients().getRemoteIpHeader()));
if (!actionId.equals(feedback.getId())) { if (!actionId.equals(feedback.getId())) {
LOG.warn( LOG.warn(

View File

@@ -29,11 +29,11 @@ import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent; import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.junit.Test; import org.junit.Test;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -28,14 +28,16 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -50,6 +52,9 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Root Poll Resource") @Stories("Root Poll Resource")
public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoDB { public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoDB {
@Autowired
private HawkbitSecurityProperties securityProperties;
@Test @Test
@Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.") @Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.")
@WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = "ROLE_CONTROLLER", autoCreateTenant = false) @WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = "ROLE_CONTROLLER", autoCreateTenant = false)
@@ -257,6 +262,23 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD
} }
@Test
@Description("Ensures that the source IP address of the polling target is not stored in repository if disabled")
public void rootRsIpAddressNotStoredIfDisabled() throws Exception {
securityProperties.getClients().setTrackRemoteIp(false);
// test
final String knownControllerId1 = "0815";
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// verify
final Target target = targetManagement.findTargetByControllerID(knownControllerId1);
assertThat(target.getTargetInfo().getAddress()).isEqualTo(IpUtil.createHttpUri("***"));
securityProperties.getClients().setTrackRemoteIp(true);
}
@Test @Test
@Description("Controller trys to finish an update process after it has been finished by an error action status.") @Description("Controller trys to finish an update process after it has been finished by an error action status.")
public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception { public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception {

View File

@@ -8,21 +8,32 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.support.RetryTemplate;;
/** /**
* The spring AMQP configuration which is enabled by using the profile * The spring AMQP configuration which is enabled by using the profile
@@ -32,14 +43,69 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class }) @EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class })
public class AmqpConfiguration { public class AmqpConfiguration {
@Autowired private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);
protected AmqpProperties amqpProperties;
@Autowired @Autowired
protected AmqpDeadletterProperties amqpDeadletterProperties; private AmqpProperties amqpProperties;
@Autowired @Autowired
private ConnectionFactory connectionFactory; private AmqpDeadletterProperties amqpDeadletterProperties;
@Autowired
@Qualifier("threadPoolExecutor")
private ThreadPoolExecutor threadPoolExecutor;
@Autowired
private ConnectionFactory rabbitConnectionFactory;
@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RabbitConnectionFactoryCreator {
@Autowired
private AmqpProperties amqpProperties;
@Autowired
@Qualifier("threadPoolExecutor")
private ThreadPoolExecutor threadPoolExecutor;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
/**
* {@link ConnectionFactory} with enabled publisher confirms and
* heartbeat.
*
* @param config
* with standard {@link RabbitProperties}
* @return {@link ConnectionFactory}
*/
@Bean
public ConnectionFactory rabbitConnectionFactory(final RabbitProperties config) {
final CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setRequestedHeartBeat(amqpProperties.getRequestedHeartBeat());
factory.setExecutor(threadPoolExecutor);
factory.getRabbitConnectionFactory().setHeartbeatExecutor(scheduledExecutorService);
factory.setPublisherConfirms(true);
final String addresses = config.getAddresses();
factory.setAddresses(addresses);
if (config.getHost() != null) {
factory.setHost(config.getHost());
factory.setPort(config.getPort());
}
if (config.getUsername() != null) {
factory.setUsername(config.getUsername());
}
if (config.getPassword() != null) {
factory.setPassword(config.getPassword());
}
if (config.getVirtualHost() != null) {
factory.setVirtualHost(config.getVirtualHost());
}
return factory;
}
}
/** /**
* Create a {@link RabbitAdmin} and ignore declaration exceptions. * Create a {@link RabbitAdmin} and ignore declaration exceptions.
@@ -49,20 +115,32 @@ public class AmqpConfiguration {
*/ */
@Bean @Bean
public RabbitAdmin rabbitAdmin() { public RabbitAdmin rabbitAdmin() {
final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); final RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
rabbitAdmin.setIgnoreDeclarationExceptions(true); rabbitAdmin.setIgnoreDeclarationExceptions(true);
return rabbitAdmin; return rabbitAdmin;
} }
/** /**
* Method to set the Jackson2JsonMessageConverter. * @return {@link RabbitTemplate} with automatic retry, published confirms
* * and {@link Jackson2JsonMessageConverter}.
* @return the Jackson2JsonMessageConverter
*/ */
@Bean @Bean
public RabbitTemplate rabbitTemplate() { public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
final RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
rabbitTemplate.setRetryTemplate(retryTemplate);
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
LOGGER.debug("Message with {} confirmed by broker.", correlationData);
} else {
LOGGER.error("Broker is unable to handle message with {} : {}", correlationData, cause);
}
});
return rabbitTemplate; return rabbitTemplate;
} }
@@ -159,7 +237,7 @@ public class AmqpConfiguration {
public SimpleRabbitListenerContainerFactory listenerContainerFactory() { public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory(); final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false); containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory); containerFactory.setConnectionFactory(rabbitConnectionFactory);
containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal()); containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
return containerFactory; return containerFactory;
} }

View File

@@ -8,6 +8,8 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -38,6 +40,11 @@ public class AmqpProperties {
*/ */
private boolean missingQueuesFatal = false; private boolean missingQueuesFatal = false;
/**
* Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}.
*/
private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60);
/** /**
* Is missingQueuesFatal enabled * Is missingQueuesFatal enabled
* *
@@ -102,4 +109,13 @@ public class AmqpProperties {
public void setReceiverQueue(final String receiverQueue) { public void setReceiverQueue(final String receiverQueue) {
this.receiverQueue = receiverQueue; this.receiverQueue = receiverQueue;
} }
public int getRequestedHeartBeat() {
return requestedHeartBeat;
}
public void setRequestedHeartBeat(final int requestedHeartBeat) {
this.requestedHeartBeat = requestedHeartBeat;
}
} }

View File

@@ -109,7 +109,7 @@ public class BaseAmqpService {
} }
protected final void logAndThrowMessageError(final Message message, final String error) { protected final void logAndThrowMessageError(final Message message, final String error) {
LOGGER.warn("Error \"{}\" reported by message: {}", error, message); LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message);
throw new IllegalArgumentException(error); throw new IllegalArgumentException(error);
} }

View File

@@ -9,10 +9,14 @@
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.net.URI; import java.net.URI;
import java.util.UUID;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
/** /**
* A default implementation for the sender service. The service sends all amqp * A default implementation for the sender service. The service sends all amqp
@@ -20,6 +24,7 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
* extracted from the uri. * extracted from the uri.
*/ */
public class DefaultAmqpSenderService implements AmqpSenderService { public class DefaultAmqpSenderService implements AmqpSenderService {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAmqpSenderService.class);
private final RabbitTemplate internalAmqpTemplate; private final RabbitTemplate internalAmqpTemplate;
@@ -39,7 +44,16 @@ public class DefaultAmqpSenderService implements AmqpSenderService {
return; return;
} }
internalAmqpTemplate.send(extractExchange(replyTo), null, message); final String correlationId = UUID.randomUUID().toString();
final String exchange = extractExchange(replyTo);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, exchange, correlationId);
} else {
LOGGER.debug("Sending message to exchange {} with correlationId {}", exchange, correlationId);
}
internalAmqpTemplate.send(exchange, null, message, new CorrelationData(correlationId));
} }
} }

View File

@@ -8,6 +8,14 @@
*/ */
package org.eclipse.hawkbit; package org.eclipse.hawkbit;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.amqp.AmqpProperties;
import org.eclipse.hawkbit.amqp.AmqpSenderService; import org.eclipse.hawkbit.amqp.AmqpSenderService;
import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService; import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
@@ -16,13 +24,22 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/** /**
* *
*/ */
@Configuration @Configuration
@EnableConfigurationProperties({ AmqpProperties.class })
public class AmqpTestConfiguration { public class AmqpTestConfiguration {
/** /**
* @return the {@link SystemSecurityContext} singleton bean which make it * @return the {@link SystemSecurityContext} singleton bean which make it
@@ -56,4 +73,55 @@ public class AmqpTestConfiguration {
public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) { public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
return new DefaultAmqpSenderService(rabbitTemplate); return new DefaultAmqpSenderService(rabbitTemplate);
} }
/**
* @return ExecutorService with security context availability in thread
* execution..
*/
@Bean
@ConditionalOnMissingBean
public Executor asyncExecutor() {
return new DelegatingSecurityContextExecutor(threadPoolExecutor());
}
/**
* @return central ThreadPoolExecutor for general purpose multi threaded
* operations. Tries an orderly shutdown when destroyed.
*/
@Bean(destroyMethod = "shutdown")
public ThreadPoolExecutor threadPoolExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS,
blockingQueue, new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
return threadPoolExecutor;
}
/**
* @return {@link TaskExecutor} for task execution
*/
@Bean
@ConditionalOnMissingBean
public TaskExecutor taskExecutor() {
return new ConcurrentTaskExecutor(asyncExecutor());
}
/**
* @return {@link ScheduledExecutorService} based on
* {@link #threadPoolTaskScheduler()}.
*/
@Bean
@ConditionalOnMissingBean
public ScheduledExecutorService scheduledExecutorService() {
return threadPoolTaskScheduler().getScheduledExecutor();
}
/**
* @return {@link ThreadPoolTaskScheduler} for scheduled operations.
*/
@Bean
@ConditionalOnMissingBean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
} }

View File

@@ -37,7 +37,7 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.util.AbstractIntegrationTestWithMongoDB; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;

View File

@@ -16,7 +16,7 @@ import org.eclipse.hawkbit.api.UrlProtocol;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.util.AbstractIntegrationTestWithMongoDB; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTestWithMongoDB;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -33,6 +33,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Test to generate the artifact download URL") @Stories("Test to generate the artifact download URL")
@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class, @SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class,
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB { public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
private static final String HTTPS_LOCALHOST = "https://localhost:8080/"; private static final String HTTPS_LOCALHOST = "https://localhost:8080/";

View File

@@ -29,12 +29,12 @@ import java.util.Set;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.util.TestdataFactory;
import org.eclipse.hawkbit.repository.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;

View File

@@ -27,7 +27,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;

View File

@@ -29,7 +29,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder; import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;

View File

@@ -41,8 +41,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo; import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;

View File

@@ -26,7 +26,7 @@ import java.util.List;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;

View File

@@ -40,7 +40,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;

View File

@@ -0,0 +1,47 @@
# hawkBit metadata repository
The repository is in charge for managing the meta data of the update server, e.g. provisioning targets, distribution sets, software modules etc.
# Build
[indent=0]
----
$ mvn clean install
----
Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
#Concepts
## Rollout
A rollout consists of the distribution set and a target-query-filter which defines the targets to be updated in this rollout. The targets within this filter are split up in configured amount of _Deployment Groups_ at creation time.
When starting a rollout, for all targets within this rollout deployment actions will be created. The deployment actions of the first group will be started immediately all other deployment actions will be scheduled.
> Due rollouts might include a large number of targets and deployment group, creation as well as starting a rollout might take some time and therefore the creation and starting of an rollout is executed asynchronously. The creation and starting progress is reflected by the rollout's status attribute
### Rollout Creation
The targets reflected by the target-query-filter is interpreted at creation time. Only targets which have been created at that time are included in the rollout. Targets which are created after the rollout creation will not be included. At rollout creation time all necessary deployment groups containing the targets will be created. Dynamically adding targets to a created or running rollout is currently not supported.
### Rollout Starting
The rollout is using the same concept based on _deployment acionts_ per target. All necessary _deployment acionts_ will be created at starting point but not all _deployment acionts_ will be set to running. Only the _deployment acionts_ of the first _deployment group_ is set to running, all other actions are created in _scheduled_ state.
If targets having not finished _deployment actions_ at rollout starting time, these action are tried to cancel (state: canceling). Scheduled actions from another rollout are canceled directly on server side because they were never running before and can be safely canceled.
>Multiple rollouts can be running at the same time, but if the rollouts effect the same targets they will interfer the targets _deployment actions_ e.g. canceling/cancel already running/scheduled _deployment actions_.
### Deployment Group Condition/Action
#### Success Condition/Action
A _success condition_ defines when a deployment group is interpreted as success and triggers the _success action_. E.g. a threshold of successfully updated targets within the group.
The _success action_ is executed if the _success_condition_ is fullfilled. Currently only the _success action_ starting the next following deployment group is available.
#### Error Condition/Action
A _error condition_ defines when a deployment group is interpreted as failure and triggers the _error action_. E.g. a threshold of update failures of targets within the group.
The _error action_ is executed if the _error condition_ is fullfilled. Currently only the _error action_ pausing the whole rollout is available, a paused rollout can be resumed by a user.
### Rollout Scheduler
The rollout is managed by a scheduler task which is checking for rollouts in _running_ state. This is done atomic by updating the row in the database, so only the rollouts are checked and processed for the current instance to avoid that multiple instances are processing a rollout.
>Due the scheduler, it might take some time until a rollouts is processed and switching is state or starting the next deployment group.

View File

@@ -0,0 +1,6 @@
# hawkBit repository API
API for repository access. Contains:
- Entity Model
- Management service API

View File

@@ -33,7 +33,7 @@ public final class Constants {
* generated by repository for every new account for "Firmware/Operating * generated by repository for every new account for "Firmware/Operating
* System" . * System" .
*/ */
public static final String SMT_DEFAULT_OS_NAME = "Firmware"; public static final String SMT_DEFAULT_OS_NAME = "OS";
/** /**
* {@link SoftwareModuleType#getName()} of a {@link SoftwareModuleType} * {@link SoftwareModuleType#getName()} of a {@link SoftwareModuleType}
* generated by repository for every new account for "applications/apps". * generated by repository for every new account for "applications/apps".

View File

@@ -0,0 +1,3 @@
# hawkBit common implementation
Core elements that can be used by implementations.

View File

@@ -1,47 +1,3 @@
# hawkBit metadata repository # hawkBit JPA implementation
The repository is in charge for managing the meta data of the update server, e.g. provisioning targets, distribution sets, software modules etc.
# Build
[indent=0]
----
$ mvn clean install
----
Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
#Concepts
## Rollout
A rollout consists of the distribution set and a target-query-filter which defines the targets to be updated in this rollout. The targets within this filter are split up in configured amount of _Deployment Groups_ at creation time.
When starting a rollout, for all targets within this rollout deployment actions will be created. The deployment actions of the first group will be started immediately all other deployment actions will be scheduled.
> Due rollouts might include a large number of targets and deployment group, creation as well as starting a rollout might take some time and therefore the creation and starting of an rollout is executed asynchronously. The creation and starting progress is reflected by the rollout's status attribute
### Rollout Creation
The targets reflected by the target-query-filter is interpreted at creation time. Only targets which have been created at that time are included in the rollout. Targets which are created after the rollout creation will not be included. At rollout creation time all necessary deployment groups containing the targets will be created. Dynamically adding targets to a created or running rollout is currently not supported.
### Rollout Starting
The rollout is using the same concept based on _deployment acionts_ per target. All necessary _deployment acionts_ will be created at starting point but not all _deployment acionts_ will be set to running. Only the _deployment acionts_ of the first _deployment group_ is set to running, all other actions are created in _scheduled_ state.
If targets having not finished _deployment actions_ at rollout starting time, these action are tried to cancel (state: canceling). Scheduled actions from another rollout are canceled directly on server side because they were never running before and can be safely canceled.
>Multiple rollouts can be running at the same time, but if the rollouts effect the same targets they will interfer the targets _deployment actions_ e.g. canceling/cancel already running/scheduled _deployment actions_.
### Deployment Group Condition/Action
#### Success Condition/Action
A _success condition_ defines when a deployment group is interpreted as success and triggers the _success action_. E.g. a threshold of successfully updated targets within the group.
The _success action_ is executed if the _success_condition_ is fullfilled. Currently only the _success action_ starting the next following deployment group is available.
#### Error Condition/Action
A _error condition_ defines when a deployment group is interpreted as failure and triggers the _error action_. E.g. a threshold of update failures of targets within the group.
The _error action_ is executed if the _error condition_ is fullfilled. Currently only the _error action_ pausing the whole rollout is available, a paused rollout can be resumed by a user.
### Rollout Scheduler
The rollout is managed by a scheduler task which is checking for rollouts in _running_ state. This is done atomic by updating the row in the database, so only the rollouts are checked and processed for the current instance to avoid that multiple instances are processing a rollout.
>Due the scheduler, it might take some time until a rollouts is processed and switching is state or starting the next deployment group.
JPA implementation of the repository. Based on [EclipseLink](http://www.eclipse.org/eclipselink/) and [Spring Data Jpa](http://projects.spring.io/spring-data-jpa/).

View File

@@ -11,8 +11,36 @@ package org.eclipse.hawkbit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.ReportManagement;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.JpaArtifactManagement;
import org.eclipse.hawkbit.repository.jpa.JpaControllerManagement;
import org.eclipse.hawkbit.repository.jpa.JpaDeploymentManagement;
import org.eclipse.hawkbit.repository.jpa.JpaDistributionSetManagement;
import org.eclipse.hawkbit.repository.jpa.JpaEntityFactory;
import org.eclipse.hawkbit.repository.jpa.JpaReportManagement;
import org.eclipse.hawkbit.repository.jpa.JpaRolloutGroupManagement;
import org.eclipse.hawkbit.repository.jpa.JpaRolloutManagement;
import org.eclipse.hawkbit.repository.jpa.JpaSoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.JpaSystemManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTagManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTargetManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler; import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager; import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
@@ -26,6 +54,7 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
@@ -35,6 +64,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@@ -50,6 +80,7 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@Configuration @Configuration
@ComponentScan @ComponentScan
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableScheduling
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* @return the {@link SystemSecurityContext} singleton bean which make it * @return the {@link SystemSecurityContext} singleton bean which make it
@@ -173,4 +204,170 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
public PlatformTransactionManager transactionManager() { public PlatformTransactionManager transactionManager() {
return new MultiTenantJpaTransactionManager(); return new MultiTenantJpaTransactionManager();
} }
/**
* {@link JpaSystemManagement} bean.
*
* @return a new {@link SystemManagement}
*/
@Bean
@ConditionalOnMissingBean
public SystemManagement systemManagement() {
return new JpaSystemManagement();
}
/**
* {@link JpaReportManagement} bean.
*
* @return a new {@link ReportManagement}
*/
@Bean
@ConditionalOnMissingBean
public ReportManagement reportManagement() {
return new JpaReportManagement();
}
/**
* {@link JpaDistributionSetManagement} bean.
*
* @return a new {@link DistributionSetManagement}
*/
@Bean
@ConditionalOnMissingBean
public DistributionSetManagement distributionSetManagement() {
return new JpaDistributionSetManagement();
}
/**
* {@link JpaTenantStatsManagement} bean.
*
* @return a new {@link TenantStatsManagement}
*/
@Bean
@ConditionalOnMissingBean
public TenantStatsManagement tenantStatsManagement() {
return new JpaTenantStatsManagement();
}
/**
* {@link JpaTenantConfigurationManagement} bean.
*
* @return a new {@link TenantConfigurationManagement}
*/
@Bean
@ConditionalOnMissingBean
public TenantConfigurationManagement tenantConfigurationManagement() {
return new JpaTenantConfigurationManagement();
}
/**
* {@link JpaTenantConfigurationManagement} bean.
*
* @return a new {@link TenantConfigurationManagement}
*/
@Bean
@ConditionalOnMissingBean
public TargetManagement targetManagement() {
return new JpaTargetManagement();
}
/**
* {@link JpaTargetFilterQueryManagement} bean.
*
* @return a new {@link TargetFilterQueryManagement}
*/
@Bean
@ConditionalOnMissingBean
public TargetFilterQueryManagement targetFilterQueryManagement() {
return new JpaTargetFilterQueryManagement();
}
/**
* {@link JpaTagManagement} bean.
*
* @return a new {@link TagManagement}
*/
@Bean
@ConditionalOnMissingBean
public TagManagement tagManagement() {
return new JpaTagManagement();
}
/**
* {@link JpaSoftwareManagement} bean.
*
* @return a new {@link SoftwareManagement}
*/
@Bean
@ConditionalOnMissingBean
public SoftwareManagement softwareManagement() {
return new JpaSoftwareManagement();
}
/**
* {@link JpaRolloutManagement} bean.
*
* @return a new {@link RolloutManagement}
*/
@Bean
@ConditionalOnMissingBean
public RolloutManagement rolloutManagement() {
return new JpaRolloutManagement();
}
/**
* {@link JpaRolloutGroupManagement} bean.
*
* @return a new {@link RolloutGroupManagement}
*/
@Bean
@ConditionalOnMissingBean
public RolloutGroupManagement rolloutGroupManagement() {
return new JpaRolloutGroupManagement();
}
/**
* {@link JpaDeploymentManagement} bean.
*
* @return a new {@link DeploymentManagement}
*/
@Bean
@ConditionalOnMissingBean
public DeploymentManagement deploymentManagement() {
return new JpaDeploymentManagement();
}
/**
* {@link JpaControllerManagement} bean.
*
* @return a new {@link ControllerManagement}
*/
@Bean
@ConditionalOnMissingBean
public ControllerManagement controllerManagement() {
return new JpaControllerManagement();
}
/**
* {@link JpaArtifactManagement} bean.
*
* @return a new {@link ArtifactManagement}
*/
@Bean
@ConditionalOnMissingBean
public ArtifactManagement artifactManagement() {
return new JpaArtifactManagement();
}
/**
* {@link JpaEntityFactory} bean.
*
* @return a new {@link EntityFactory}
*/
@Bean
@ConditionalOnMissingBean
public EntityFactory entityFactory() {
return new JpaEntityFactory();
}
} }

View File

@@ -16,7 +16,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -82,15 +84,26 @@ public interface DistributionSetRepository
List<DistributionSet> findByModules(JpaSoftwareModule module); List<DistributionSet> findByModules(JpaSoftwareModule module);
/** /**
* Finds {@link DistributionSet}s based on given ID if they are not assigned * Finds {@link DistributionSet}s based on given ID that are assigned yet to
* yet to an {@link UpdateAction}, i.e. unused. * an {@link Action}, i.e. in use.
* *
* @param ids * @param ids
* to search for * to search for
* @return * @return list of {@link DistributionSet#getId()}
*/ */
@Query("select ac.distributionSet.id from JpaAction ac where ac.distributionSet.id in :ids") @Query("select ac.distributionSet.id from JpaAction ac where ac.distributionSet.id in :ids")
List<Long> findAssignedDistributionSetsById(@Param("ids") Long... ids); List<Long> findAssignedToTargetDistributionSetsById(@Param("ids") Long... ids);
/**
* Finds {@link DistributionSet}s based on given ID that are assigned yet to
* an {@link Rollout}, i.e. in use.
*
* @param ids
* to search for
* @return list of {@link DistributionSet#getId()}
*/
@Query("select ra.distributionSet.id from JpaRollout ra where ra.distributionSet.id in :ids")
List<Long> findAssignedToRolloutDistributionSetsById(@Param("ids") Long... ids);
/** /**
* Saves all given {@link DistributionSet}s. * Saves all given {@link DistributionSet}s.

View File

@@ -41,7 +41,6 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -52,7 +51,6 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaArtifactManagement implements ArtifactManagement { public class JpaArtifactManagement implements ArtifactManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class); private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class);

View File

@@ -54,7 +54,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -65,7 +64,6 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaControllerManagement implements ControllerManagement { public class JpaControllerManagement implements ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class); private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");

View File

@@ -96,7 +96,6 @@ import com.google.common.eventbus.EventBus;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaDeploymentManagement implements DeploymentManagement { public class JpaDeploymentManagement implements DeploymentManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaDeploymentManagement.class); private static final Logger LOG = LoggerFactory.getLogger(JpaDeploymentManagement.class);

View File

@@ -60,7 +60,6 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -74,7 +73,6 @@ import com.google.common.eventbus.EventBus;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaDistributionSetManagement implements DistributionSetManagement { public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired @Autowired
@@ -173,7 +171,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
public void deleteDistributionSet(final Long... distributionSetIDs) { public void deleteDistributionSet(final Long... distributionSetIDs) {
final List<Long> toHardDelete = new ArrayList<>(); final List<Long> toHardDelete = new ArrayList<>();
final List<Long> assigned = distributionSetRepository.findAssignedDistributionSetsById(distributionSetIDs); final List<Long> assigned = distributionSetRepository
.findAssignedToTargetDistributionSetsById(distributionSetIDs);
assigned.addAll(distributionSetRepository.findAssignedToRolloutDistributionSetsById(distributionSetIDs));
// soft delete assigned // soft delete assigned
if (!assigned.isEmpty()) { if (!assigned.isEmpty()) {

View File

@@ -42,13 +42,11 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.stereotype.Service;
/** /**
* JPA Implementation of {@link EntityFactory}. * JPA Implementation of {@link EntityFactory}.
* *
*/ */
@Service
public class JpaEntityFactory implements EntityFactory { public class JpaEntityFactory implements EntityFactory {
@Override @Override

View File

@@ -45,7 +45,6 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -56,7 +55,6 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaReportManagement implements ReportManagement { public class JpaReportManagement implements ReportManagement {
@Value("${spring.jpa.database}") @Value("${spring.jpa.database}")

View File

@@ -47,7 +47,6 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -56,7 +55,6 @@ import org.springframework.validation.annotation.Validated;
* JPA implementation of {@link RolloutGroupManagement}. * JPA implementation of {@link RolloutGroupManagement}.
*/ */
@Validated @Validated
@Service
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class JpaRolloutGroupManagement implements RolloutGroupManagement { public class JpaRolloutGroupManagement implements RolloutGroupManagement {

View File

@@ -60,8 +60,6 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
@@ -76,8 +74,6 @@ import org.springframework.validation.annotation.Validated;
* JPA implementation of {@link RolloutManagement}. * JPA implementation of {@link RolloutManagement}.
*/ */
@Validated @Validated
@Service
@EnableScheduling
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class JpaRolloutManagement implements RolloutManagement { public class JpaRolloutManagement implements RolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class); private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);

View File

@@ -60,7 +60,6 @@ import org.springframework.data.domain.SliceImpl;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -74,7 +73,6 @@ import com.google.common.collect.Sets;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaSoftwareManagement implements SoftwareManagement { public class JpaSoftwareManagement implements SoftwareManagement {
@Autowired @Autowired

View File

@@ -8,7 +8,6 @@
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -34,10 +33,8 @@ import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -49,7 +46,6 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement { public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement {
@Autowired @Autowired
private EntityManager entityManager; private EntityManager entityManager;
@@ -108,7 +104,11 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired @Autowired
private TenancyCacheManager cacheManager; private TenancyCacheManager cacheManager;
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>(); @Autowired
private SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator;
@Autowired
private ApplicationContext applicationContext;
@Override @Override
public SystemUsageReport getSystemUsageStatistics() { public SystemUsageReport getSystemUsageStatistics() {
@@ -154,9 +154,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Override @Override
@Transactional(propagation = Propagation.SUPPORTS) @Transactional(propagation = Propagation.SUPPORTS)
@Bean
public KeyGenerator currentTenantKeyGenerator() { public KeyGenerator currentTenantKeyGenerator() {
return new CurrentTenantKeyGenerator(); return currentTenantCacheKeyGenerator.currentTenantKeyGenerator();
} }
@Override @Override
@@ -169,11 +168,12 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
// Create if it does not exist // Create if it does not exist
if (result == null) { if (result == null) {
try { try {
createInitialTenant.set(tenant); currentTenantCacheKeyGenerator.getCreateInitialTenant().set(tenant);
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null)); cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
applicationContext.getBean("currentTenantKeyGenerator");
return tenantMetaDataRepository.save(new JpaTenantMetaData(createStandardSoftwareDataSetup(), tenant)); return tenantMetaDataRepository.save(new JpaTenantMetaData(createStandardSoftwareDataSetup(), tenant));
} finally { } finally {
createInitialTenant.remove(); currentTenantCacheKeyGenerator.getCreateInitialTenant().remove();
} }
} }
@@ -239,7 +239,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
// tenant is not cached anyway already. // tenant is not cached anyway already.
@Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.READ_UNCOMMITTED) @Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.READ_UNCOMMITTED)
public String currentTenant() { public String currentTenant() {
final String initialTenantCreation = createInitialTenant.get(); final String initialTenantCreation = currentTenantCacheKeyGenerator.getCreateInitialTenant().get();
if (initialTenantCreation == null) { if (initialTenantCreation == null) {
final TenantMetaData findByTenant = tenantMetaDataRepository final TenantMetaData findByTenant = tenantMetaDataRepository
.findByTenantIgnoreCase(tenantAware.getCurrentTenant()); .findByTenantIgnoreCase(tenantAware.getCurrentTenant());
@@ -260,29 +260,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
return tenantMetaDataRepository.save((JpaTenantMetaData) metaData); return tenantMetaDataRepository.save((JpaTenantMetaData) metaData);
} }
/**
* A implementation of the {@link KeyGenerator} to generate a key based on
* either the {@code createInitialTenant} thread local and the
* {@link TenantAware}, but in case we are in a tenant creation with its
* default types we need to use the tenant the current tenant which is
* currently created and not the one currently in the {@link TenantAware}.
*
*/
public class CurrentTenantKeyGenerator implements KeyGenerator {
@Override
// Exception squid:S923 - override
@SuppressWarnings({ "squid:S923" })
public Object generate(final Object target, final Method method, final Object... params) {
final String initialTenantCreation = createInitialTenant.get();
if (initialTenantCreation == null) {
return SimpleKeyGenerator.generateKey(tenantAware.getCurrentTenant().toUpperCase(),
tenantAware.getCurrentTenant().toUpperCase());
}
return SimpleKeyGenerator.generateKey(initialTenantCreation.toUpperCase(),
initialTenantCreation.toUpperCase());
}
}
private DistributionSetType createStandardSoftwareDataSetup() { private DistributionSetType createStandardSoftwareDataSetup() {
final SoftwareModuleType app = softwareModuleTypeRepository final SoftwareModuleType app = softwareModuleTypeRepository
.save(new JpaSoftwareModuleType(Constants.SMT_DEFAULT_APP_KEY, Constants.SMT_DEFAULT_APP_NAME, .save(new JpaSoftwareModuleType(Constants.SMT_DEFAULT_APP_KEY, Constants.SMT_DEFAULT_APP_NAME,

View File

@@ -39,7 +39,6 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -52,7 +51,6 @@ import com.google.common.eventbus.EventBus;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaTagManagement implements TagManagement { public class JpaTagManagement implements TagManagement {
@Autowired @Autowired

View File

@@ -40,7 +40,6 @@ import com.google.common.base.Strings;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement { public class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
@Autowired @Autowired

View File

@@ -59,7 +59,6 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@@ -75,7 +74,6 @@ import com.google.common.eventbus.EventBus;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaTargetManagement implements TargetManagement { public class JpaTargetManagement implements TargetManagement {
@Autowired @Autowired

View File

@@ -23,7 +23,6 @@ import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -33,7 +32,6 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class JpaTenantConfigurationManagement implements EnvironmentAware, TenantConfigurationManagement { public class JpaTenantConfigurationManagement implements EnvironmentAware, TenantConfigurationManagement {
@Autowired @Autowired

View File

@@ -24,7 +24,6 @@ import org.springframework.validation.annotation.Validated;
* *
*/ */
@Validated @Validated
@Service
public class JpaTenantStatsManagement implements TenantStatsManagement { public class JpaTenantStatsManagement implements TenantStatsManagement {
@Autowired @Autowired

View File

@@ -0,0 +1,65 @@
/**
* 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.repository.jpa;
import java.lang.reflect.Method;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
/**
* Implementation of {@link CurrentTenantCacheKeyGenerator}.
*
*/
@Service
public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyGenerator {
@Autowired
private TenantAware tenantAware;
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>();
/**
* A implementation of the {@link KeyGenerator} to generate a key based on
* either the {@code createInitialTenant} thread local and the
* {@link TenantAware}, but in case we are in a tenant creation with its
* default types we need to use the tenant the current tenant which is
* currently created and not the one currently in the {@link TenantAware}.
*
*/
public class CurrentTenantKeyGenerator implements KeyGenerator {
@Override
// Exception squid:S923 - override
@SuppressWarnings({ "squid:S923" })
public Object generate(final Object target, final Method method, final Object... params) {
final String initialTenantCreation = createInitialTenant.get();
if (initialTenantCreation == null) {
return SimpleKeyGenerator.generateKey(tenantAware.getCurrentTenant().toUpperCase(),
tenantAware.getCurrentTenant().toUpperCase());
}
return SimpleKeyGenerator.generateKey(initialTenantCreation.toUpperCase(),
initialTenantCreation.toUpperCase());
}
}
@Override
@Bean
public KeyGenerator currentTenantKeyGenerator() {
return new CurrentTenantKeyGenerator();
}
ThreadLocal<String> getCreateInitialTenant() {
return createInitialTenant;
}
}

View File

@@ -12,7 +12,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.util.AbstractIntegrationTest; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.mongodb.gridfs.GridFsOperations; import org.springframework.data.mongodb.gridfs.GridFsOperations;

View File

@@ -12,7 +12,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.util.AbstractIntegrationTestWithMongoDB; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTestWithMongoDB;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;

View File

@@ -32,8 +32,8 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider; import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test; import org.junit.Test;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Criteria;

View File

@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -37,9 +38,15 @@ import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSe
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.fest.assertions.core.Condition; import org.fest.assertions.core.Condition;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -777,36 +784,55 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
} }
@Test @Test
@Description("Deltes a DS that is no in use. Expected behaviour is a soft delete on the database, i.e. only marked as " @Description("Deletes a DS that is in use by either target assignment or rollout. Expected behaviour is a soft delete on the database, i.e. only marked as "
+ "deleted, kept eas refernce and unavailable for future use..") + "deleted, kept as reference but unavailable for future use..")
public void deleteAssignedDistributionSet() { public void deleteAssignedDistributionSet() {
DistributionSet ds1 = testdataFactory.createDistributionSet("ds-1"); DistributionSet ds1 = testdataFactory.createDistributionSet("ds-1");
DistributionSet ds2 = testdataFactory.createDistributionSet("ds-2"); DistributionSet ds2 = testdataFactory.createDistributionSet("ds-2");
DistributionSet dsAssigned = testdataFactory.createDistributionSet("ds-3"); DistributionSet dsToTargetAssigned = testdataFactory.createDistributionSet("ds-3");
final DistributionSet dsToRolloutAssigned = testdataFactory.createDistributionSet("ds-4");
ds1 = distributionSetManagement.findDistributionSetByNameAndVersion(ds1.getName(), ds1.getVersion()); ds1 = distributionSetManagement.findDistributionSetByNameAndVersion(ds1.getName(), ds1.getVersion());
ds2 = distributionSetManagement.findDistributionSetByNameAndVersion(ds2.getName(), ds2.getVersion()); ds2 = distributionSetManagement.findDistributionSetByNameAndVersion(ds2.getName(), ds2.getVersion());
// create assigned DS // create assigned DS
dsAssigned = distributionSetManagement.findDistributionSetByNameAndVersion(dsAssigned.getName(), dsToTargetAssigned = distributionSetManagement.findDistributionSetByNameAndVersion(dsToTargetAssigned.getName(),
dsAssigned.getVersion()); dsToTargetAssigned.getVersion());
final Target target = new JpaTarget("4712"); final Target target = new JpaTarget("4712");
final Target savedTarget = targetManagement.createTarget(target); final Target savedTarget = targetManagement.createTarget(target);
final List<Target> toAssign = new ArrayList<>(); final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget); toAssign.add(savedTarget);
deploymentManagement.assignDistributionSet(dsAssigned, toAssign); deploymentManagement.assignDistributionSet(dsToTargetAssigned, toAssign);
// delete a ds // create assigned rollout
assertThat(distributionSetRepository.findAll()).hasSize(3); createRolloutByVariables("test", "test", 5, "name==*", dsToRolloutAssigned, "50", "5");
distributionSetManagement.deleteDistributionSet(dsAssigned.getId());
// delete assigned ds
assertThat(distributionSetRepository.findAll()).hasSize(4);
distributionSetManagement.deleteDistributionSet(dsToTargetAssigned.getId(), dsToRolloutAssigned.getId());
// not assigned so not marked as deleted // not assigned so not marked as deleted
assertThat(distributionSetRepository.findAll()).hasSize(3); assertThat(distributionSetRepository.findAll()).hasSize(4);
assertThat(distributionSetManagement assertThat(distributionSetManagement
.findDistributionSetsByDeletedAndOrCompleted(pageReq, Boolean.FALSE, Boolean.TRUE).getTotalElements()) .findDistributionSetsByDeletedAndOrCompleted(pageReq, Boolean.FALSE, Boolean.TRUE).getTotalElements())
.isEqualTo(2); .isEqualTo(2);
} }
private Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
final String successCondition, final String errorCondition) {
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
final Rollout rolloutToCreate = new JpaRollout();
rolloutToCreate.setName(rolloutName);
rolloutToCreate.setDescription(rolloutDescription);
rolloutToCreate.setTargetFilterQuery(filterQuery);
rolloutToCreate.setDistributionSet(distributionSet);
return rolloutManagement.createRollout(rolloutToCreate, groupSize, conditions);
}
private Target sendUpdateActionStatusToTarget(final Status status, final Action updActA, final Target t, private Target sendUpdateActionStatusToTarget(final Status status, final Action updActA, final Target t,
final String... msgs) { final String... msgs) {
updActA.setStatus(status); updActA.setStatus(status);

View File

@@ -37,9 +37,9 @@ import org.eclipse.hawkbit.repository.report.model.DataReportSeries;
import org.eclipse.hawkbit.repository.report.model.DataReportSeriesItem; import org.eclipse.hawkbit.repository.report.model.DataReportSeriesItem;
import org.eclipse.hawkbit.repository.report.model.InnerOuterDataReportSeries; import org.eclipse.hawkbit.repository.report.model.InnerOuterDataReportSeries;
import org.eclipse.hawkbit.repository.report.model.SeriesTime; import org.eclipse.hawkbit.repository.report.model.SeriesTime;
import org.eclipse.hawkbit.repository.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -34,13 +34,13 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder; import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions; import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.util.TestdataFactory;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Description; import org.springframework.context.annotation.Description;

View File

@@ -39,7 +39,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;

View File

@@ -18,7 +18,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.report.model.TenantUsage; import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.repository.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.junit.Test; import org.junit.Test;
import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Description;

View File

@@ -38,13 +38,13 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetIdName; import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.util.WithUser;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;

View File

@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;

View File

@@ -16,7 +16,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;

View File

@@ -18,7 +18,7 @@ import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields; import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -34,7 +35,7 @@ public class RSQLSoftwareModuleTypeFieldsTest extends AbstractJpaIntegrationTest
@Test @Test
@Description("Test filter software module test type by name") @Description("Test filter software module test type by name")
public void testFilterByParameterName() { public void testFilterByParameterName() {
assertRSQLQuery(SoftwareModuleTypeFields.NAME.name() + "==Firmware", 1); assertRSQLQuery(SoftwareModuleTypeFields.NAME.name() + "==" + Constants.SMT_DEFAULT_OS_NAME, 1);
} }
@Test @Test

View File

@@ -24,7 +24,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;

View File

@@ -16,8 +16,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;

View File

@@ -0,0 +1,42 @@
/**
* 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.repository.test.matcher;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.hamcrest.Factory;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
/**
* Matcher for {@link BaseEntity}.
*/
public class BaseEntityMatcher {
private BaseEntityMatcher() {
}
@Factory
public static Matcher<BaseEntity> hasId(final Long id) {
return new HasIdMatcher(Matchers.equalTo(id));
}
private static class HasIdMatcher extends FeatureMatcher<BaseEntity, Long> {
public HasIdMatcher(final Matcher<Long> subMatcher) {
super(subMatcher, "getId()", "id");
}
@Override
protected Long featureValueOf(final BaseEntity baseEntity) {
return baseEntity.getId();
}
}
}

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter; import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.io.IOException; import java.io.IOException;
import java.net.UnknownHostException; import java.net.UnknownHostException;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.io.File; import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
/** /**
* Repository support for tests. * Repository support for tests.

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
/** /**
* *

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.util; package org.eclipse.hawkbit.repository.test.util;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited; import java.lang.annotation.Inherited;

View File

@@ -8,7 +8,7 @@
*/ */
package org.eclipse.hawkbit.rest; package org.eclipse.hawkbit.rest;
import org.eclipse.hawkbit.repository.util.AbstractIntegrationTest; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.rest.configuration.RestConfiguration; import org.eclipse.hawkbit.rest.configuration.RestConfiguration;
import org.eclipse.hawkbit.rest.util.FilterHttpResponse; import org.eclipse.hawkbit.rest.util.FilterHttpResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -8,7 +8,7 @@
*/ */
package org.eclipse.hawkbit.rest; package org.eclipse.hawkbit.rest;
import org.eclipse.hawkbit.repository.util.AbstractIntegrationTestWithMongoDB; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.configuration.RestConfiguration; import org.eclipse.hawkbit.rest.configuration.RestConfiguration;
import org.eclipse.hawkbit.rest.util.FilterHttpResponse; import org.eclipse.hawkbit.rest.util.FilterHttpResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -17,7 +17,7 @@ import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
/** /**
* * Service to check permissions.
* *
*/ */
public class PermissionService { public class PermissionService {

View File

@@ -0,0 +1,52 @@
/**
* 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.im.authentication;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
/**
* Utility method for creation of <tt>GrantedAuthority</tt> collections etc.
*/
public final class PermissionUtils {
private PermissionUtils() {
}
/**
* Create {@link GrantedAuthority} by a special role.
*
* @param roles
* the roles
* @return a list of {@link GrantedAuthority}
*/
public static List<GrantedAuthority> createAuthorityList(final Collection<String> roles) {
final List<GrantedAuthority> authorities = new ArrayList<>(roles.size());
for (final String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
/**
* Returns all authorities.
*
* @return a list of {@link GrantedAuthority}
*/
public static List<GrantedAuthority> createAllAuthorityList() {
return createAuthorityList(SpPermission.getAllAuthorities());
}
}

View File

@@ -9,7 +9,16 @@
package org.eclipse.hawkbit.im.authentication; package org.eclipse.hawkbit.im.authentication;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
@@ -35,6 +44,8 @@ import org.springframework.security.core.GrantedAuthority;
*/ */
public final class SpPermission { public final class SpPermission {
private static final Logger LOGGER = LoggerFactory.getLogger(SpPermission.class);
/** /**
* Permission to read the targets from the * Permission to read the targets from the
* {@link ProvisioningTargetRepository} including their meta information, * {@link ProvisioningTargetRepository} including their meta information,
@@ -139,6 +150,53 @@ public final class SpPermission {
// Constants only // Constants only
} }
/**
* Return all permission.
*
* @return all permission
*/
public static Collection<String> getAllAuthorities() {
return getAllAuthorities(Collections.emptyList());
}
/**
* Return all permission.
*
* @param exclusionRoles
* roles which will excluded
* @return all permissions
*/
public static Collection<String> getAllAuthorities(final String... exclusionRoles) {
return getAllAuthorities(Arrays.asList(exclusionRoles));
}
/**
* Return all permission.
*
* @param exclusionRoles
* roles which will excluded
* @return all permissions
*/
public static Collection<String> getAllAuthorities(final Collection<String> exclusionRoles) {
final List<String> allPermissions = new ArrayList<>();
final Field[] declaredFields = SpPermission.class.getDeclaredFields();
for (final Field field : declaredFields) {
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
try {
final String role = (String) field.get(null);
if (!(exclusionRoles.contains(role))) {
allPermissions.add(role);
}
} catch (final IllegalAccessException e) {
LOGGER.error(e.getMessage(), e);
}
}
}
return allPermissions;
}
/** /**
* Contains all the spring security evaluation expressions for the * Contains all the spring security evaluation expressions for the
* {@link PreAuthorize} annotation for method security. * {@link PreAuthorize} annotation for method security.

View File

@@ -8,8 +8,10 @@
*/ */
package org.eclipse.hawkbit.security; package org.eclipse.hawkbit.security;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.hawkbit.security.SecurityConstants.SECURITY_LOG_PREFIX;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -31,25 +33,21 @@ import com.google.common.cache.CacheBuilder;
/** /**
* Filter for protection against denial of service attacks. It reduces the * Filter for protection against denial of service attacks. It reduces the
* maximum number of request per seconds which can be separately configured for * maximum number of request per seconds which can be separately configured for
* read (GET) and write (PUT/POST/DELETE) requests. requests * read (GET) and write (PUT/POST/DELETE) requests.
*
*
*
*
*/ */
public class DosFilter extends OncePerRequestFilter { public class DosFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(DosFilter.class); private static final Logger LOG = LoggerFactory.getLogger(DosFilter.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); private static final Logger LOG_DOS = LoggerFactory.getLogger(SECURITY_LOG_PREFIX + ".dos");
private static final Logger LOG_BLACKLIST = LoggerFactory.getLogger("server-security.blacklist"); private static final Logger LOG_BLACKLIST = LoggerFactory.getLogger(SECURITY_LOG_PREFIX + ".blacklist");
private final Pattern ipAdressBlacklist; private final Pattern ipAdressBlacklist;
private final Cache<String, AtomicInteger> readCountCache = CacheBuilder.newBuilder() private final Cache<String, AtomicInteger> readCountCache = CacheBuilder.newBuilder().expireAfterAccess(1, SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS).build(); .build();
private final Cache<String, AtomicInteger> writeCountCache = CacheBuilder.newBuilder() private final Cache<String, AtomicInteger> writeCountCache = CacheBuilder.newBuilder().expireAfterAccess(1, SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS).build(); .build();
private final Integer maxRead; private final Integer maxRead;
private final Integer maxWrite; private final Integer maxWrite;
@@ -78,7 +76,7 @@ public class DosFilter extends OncePerRequestFilter {
*/ */
public DosFilter(final Integer maxRead, final Integer maxWrite, final String ipDosWhiteListPattern, public DosFilter(final Integer maxRead, final Integer maxWrite, final String ipDosWhiteListPattern,
final String ipBlackListPattern, final String forwardHeader) { final String ipBlackListPattern, final String forwardHeader) {
super();
this.maxRead = maxRead; this.maxRead = maxRead;
this.maxWrite = maxWrite; this.maxWrite = maxWrite;
this.forwardHeader = forwardHeader; this.forwardHeader = forwardHeader;
@@ -96,21 +94,13 @@ public class DosFilter extends OncePerRequestFilter {
} }
} }
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(
* javax.servlet.http. HttpServletRequest,
* javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
*/
@Override @Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException { final FilterChain filterChain) throws ServletException, IOException {
boolean processChain; boolean processChain;
final String ip = IpUtil.getClientIpFromRequest(request, forwardHeader).getHost(); final String ip = IpUtil.getClientIpFromRequest(request, forwardHeader, true).getHost();
if (checkIpFails(ip)) { if (checkIpFails(ip)) {
processChain = handleMissingIpAddress(response); processChain = handleMissingIpAddress(response);
} else { } else {
@@ -152,11 +142,9 @@ public class DosFilter extends OncePerRequestFilter {
} }
private static boolean handleMissingIpAddress(final HttpServletResponse response) { private static boolean handleMissingIpAddress(final HttpServletResponse response) {
boolean processChain;
LOG.error("Failed to get peer IP adress"); LOG.error("Failed to get peer IP adress");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
processChain = false; return false;
return processChain;
} }
private boolean handleWriteRequest(final HttpServletResponse response, final String ip) { private boolean handleWriteRequest(final HttpServletResponse response, final String ip) {

View File

@@ -82,10 +82,16 @@ public class HawkbitSecurityProperties {
private String blacklist = ""; private String blacklist = "";
/** /**
* Name of the http header from which the remote ip is extracted. * Name of the http header from which the remote ip is extracted for DDI
* connected clients.
*/ */
private String remoteIpHeader = "X-Forwarded-For"; private String remoteIpHeader = "X-Forwarded-For";
/**
* Set to <code>true</code> if DDI clients remote IP should be stored.
*/
private boolean trackRemoteIp = true;
public String getBlacklist() { public String getBlacklist() {
return blacklist; return blacklist;
} }
@@ -101,6 +107,14 @@ public class HawkbitSecurityProperties {
public void setRemoteIpHeader(final String remoteIpHeader) { public void setRemoteIpHeader(final String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader; this.remoteIpHeader = remoteIpHeader;
} }
public boolean isTrackRemoteIp() {
return trackRemoteIp;
}
public void setTrackRemoteIp(final boolean trackRemoteIp) {
this.trackRemoteIp = trackRemoteIp;
}
} }
/** /**

View File

@@ -0,0 +1,24 @@
/**
* 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;
/**
* Constants related to security.
*/
public final class SecurityConstants {
/**
* Logger prefix used for security logging.
*/
public static final String SECURITY_LOG_PREFIX = "server-security";
private SecurityConstants() {
}
}

View File

@@ -14,12 +14,8 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
/** /**
* Auditor class that allows {@link BaseEntity}s to insert currenlt logged in * Auditor class that allows BaseEntitys to insert current logged in user for
* user for repository changes. * repository changes.
*
*
*
*
* *
*/ */
public class SpringSecurityAuditorAware implements AuditorAware<String> { public class SpringSecurityAuditorAware implements AuditorAware<String> {
@@ -29,16 +25,21 @@ public class SpringSecurityAuditorAware implements AuditorAware<String> {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) { if (isAuthenticationInvalid(authentication)) {
return null; return null;
} }
if (authentication.getPrincipal() != null) { return getCurrentAuditor(authentication);
if (authentication.getPrincipal() instanceof UserDetails) { }
return ((UserDetails) authentication.getPrincipal()).getUsername();
} private String getCurrentAuditor(final Authentication authentication) {
return authentication.getPrincipal().toString(); if (authentication.getPrincipal() instanceof UserDetails) {
return ((UserDetails) authentication.getPrincipal()).getUsername();
} }
return null; return authentication.getPrincipal().toString();
}
private static boolean isAuthenticationInvalid(final Authentication authentication) {
return authentication == null || !authentication.isAuthenticated() || authentication.getPrincipal() == null;
} }
} }

View File

@@ -15,6 +15,8 @@ import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import com.google.common.net.HttpHeaders; import com.google.common.net.HttpHeaders;
/** /**
@@ -45,17 +47,49 @@ public final class IpUtil {
* @param request * @param request
* the {@link HttpServletRequest} to determine the IP address * the {@link HttpServletRequest} to determine the IP address
* where this request has been sent from * where this request has been sent from
* @param forwardHeader * @param securityProperties
* the header name containing the IP address e.g. forwarded by a * hawkBit security properties.
* proxy {@code x-forwarded-for}
* @return the {@link URI} based IP address from the client which sent the * @return the {@link URI} based IP address from the client which sent the
* request * request
*/ */
public static URI getClientIpFromRequest(final HttpServletRequest request, final String forwardHeader) { public static URI getClientIpFromRequest(final HttpServletRequest request,
String ip = request.getHeader(forwardHeader); final HawkbitSecurityProperties securityProperties) {
if (ip == null || (ip = findClientIpAddress(ip)) == null) {
ip = request.getRemoteAddr(); return getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader(),
securityProperties.getClients().isTrackRemoteIp());
}
/**
* Retrieves the string based IP address from a given
* {@link HttpServletRequest} by either the
* {@link HttpHeaders#X_FORWARDED_FOR} or by the
* {@link HttpServletRequest#getRemoteAddr()} methods.
*
* @param request
* the {@link HttpServletRequest} to determine the IP address
* where this request has been sent from
* @param forwardHeader
* the header name containing the IP address e.g. forwarded by a
* proxy {@code x-forwarded-for}
*
* @param trackRemoteIp
* to <code>true</code> if remote IP should be tracked.
* @return the {@link URI} based IP address from the client which sent the
* request
*/
public static URI getClientIpFromRequest(final HttpServletRequest request, final String forwardHeader,
final boolean trackRemoteIp) {
String ip;
if (trackRemoteIp) {
ip = request.getHeader(forwardHeader);
if (ip == null || (ip = findClientIpAddress(ip)) == null) {
ip = request.getRemoteAddr();
}
} else {
ip = "***";
} }
return createHttpUri(ip); return createHttpUri(ip);
} }

View File

@@ -0,0 +1,54 @@
/**
* 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.im.authentication;
import static org.fest.assertions.api.Assertions.assertThat;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.springframework.context.annotation.Description;
import org.springframework.security.core.GrantedAuthority;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test {@link SpPermission}.
*/
@Features("Unit Tests - Security")
@Stories("Permission Test")
public final class PermissionTest {
@Test
@Description("Verify the get permission function")
public void testGetPermissions() {
final int allPermission = 15;
final int permissionWithoutSystem = allPermission - 3;
final Collection<String> allAuthorities = SpPermission.getAllAuthorities();
final List<GrantedAuthority> allAuthoritiesList = PermissionUtils.createAllAuthorityList();
assertThat(allAuthorities).hasSize(allPermission);
assertThat(allAuthoritiesList).hasSize(allPermission);
assertThat(allAuthoritiesList.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
.containsAll(allAuthorities);
final Collection<String> authoritiesWithoutSystem = SpPermission.getAllAuthorities(SpPermission.SYSTEM_ADMIN,
SpPermission.SYSTEM_DIAG, SpPermission.SYSTEM_MONITOR);
final List<GrantedAuthority> authoritiesListWithoutSystem = PermissionUtils.createAuthorityList(SpPermission
.getAllAuthorities(SpPermission.SYSTEM_ADMIN, SpPermission.SYSTEM_DIAG, SpPermission.SYSTEM_MONITOR));
assertThat(authoritiesWithoutSystem).hasSize(permissionWithoutSystem);
assertThat(authoritiesListWithoutSystem).hasSize(permissionWithoutSystem);
assertThat(authoritiesListWithoutSystem.stream().map(authority -> authority.getAuthority())
.collect(Collectors.toList())).containsAll(authoritiesWithoutSystem);
}
}

View File

@@ -50,7 +50,7 @@ public class IpUtilTest {
when(requestMock.getRemoteAddr()).thenReturn(knownRemoteClientIP.getHost()); when(requestMock.getRemoteAddr()).thenReturn(knownRemoteClientIP.getHost());
// test // test
final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "bumlux"); final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "bumlux", true);
// verify // verify
assertThat(remoteAddr).as("The remote address should be as the known client IP address") assertThat(remoteAddr).as("The remote address should be as the known client IP address")
@@ -59,6 +59,25 @@ public class IpUtilTest {
verify(requestMock, times(1)).getRemoteAddr(); verify(requestMock, times(1)).getRemoteAddr();
} }
@Test
@Description("Tests create uri from request with masked IP when IP tracking is disabled")
public void maskRemoteAddrIfDisabled() {
// known values
final URI knownRemoteClientIP = IpUtil.createHttpUri("***");
// mock
when(requestMock.getHeader(HttpHeaders.X_FORWARDED_FOR)).thenReturn(null);
when(requestMock.getRemoteAddr()).thenReturn(knownRemoteClientIP.getHost());
// test
final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "bumlux", false);
// verify
assertThat(remoteAddr).as("The remote address should be as the known client IP address")
.isEqualTo(knownRemoteClientIP);
verify(requestMock, times(0)).getHeader("bumlux");
verify(requestMock, times(0)).getRemoteAddr();
}
@Test @Test
@Description("Tests create uri from x forward header") @Description("Tests create uri from x forward header")
public void getRemoteAddrFromXForwardedForHeader() { public void getRemoteAddrFromXForwardedForHeader() {
@@ -69,7 +88,7 @@ public class IpUtilTest {
when(requestMock.getRemoteAddr()).thenReturn(null); when(requestMock.getRemoteAddr()).thenReturn(null);
// test // test
final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "X-Forwarded-For"); final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "X-Forwarded-For", true);
// verify // verify
assertThat(remoteAddr).as("The remote address should be as the known client IP address") assertThat(remoteAddr).as("The remote address should be as the known client IP address")

View File

@@ -208,6 +208,7 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
* (controller Id, name & description) and action buttons layout * (controller Id, name & description) and action buttons layout
*/ */
addStyleName("lay-color"); addStyleName("lay-color");
setSizeUndefined();
formLayout = new FormLayout(); formLayout = new FormLayout();
formLayout.setCaption(null); formLayout.setCaption(null);

View File

@@ -330,6 +330,8 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
getColorPickerLayout().getSelPreview().setColor(getColorPickerLayout().getSelectedColor()); getColorPickerLayout().getSelPreview().setColor(getColorPickerLayout().getSelectedColor());
mainLayout.addComponent(colorPickerLayout, 1, 0); mainLayout.addComponent(colorPickerLayout, 1, 0);
mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER); mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER);
} else {
mainLayout.removeComponent(colorPickerLayout);
} }
tagPreviewBtnClicked = !tagPreviewBtnClicked; tagPreviewBtnClicked = !tagPreviewBtnClicked;
} }

View File

@@ -53,70 +53,65 @@ public class ArtifactUploadState implements ManagmentEntityState<Long>, Serializ
private Set<Long> selectedSoftwareModules = Collections.emptySet(); private Set<Long> selectedSoftwareModules = Collections.emptySet();
private boolean swTypeFilterClosed = Boolean.FALSE; private boolean swTypeFilterClosed;
private boolean swModuleTableMaximized = Boolean.FALSE; private boolean swModuleTableMaximized;
private boolean artifactDetailsMaximized = Boolean.FALSE; private boolean artifactDetailsMaximized;
private final Set<String> selectedDeleteSWModuleTypes = new HashSet<>(); private final Set<String> selectedDeleteSWModuleTypes = new HashSet<>();
private boolean noDataAvilableSoftwareModule = Boolean.FALSE; private boolean noDataAvilableSoftwareModule;
private boolean isStatusPopupMinimized = Boolean.FALSE; private boolean statusPopupMinimized;
private boolean isUploadCompleted = Boolean.FALSE; private boolean uploadCompleted;
private List<UploadStatusObject> uploadedFileStatusList = new ArrayList<>(); private List<UploadStatusObject> uploadedFileStatusList = new ArrayList<>();
private final AtomicInteger numberOfFileUploadsExpected = new AtomicInteger(); private final AtomicInteger numberOfFileUploadsExpected = new AtomicInteger();
private final AtomicInteger numberOfFilesActuallyUpload = new AtomicInteger(); private final AtomicInteger numberOfFilesActuallyUpload = new AtomicInteger();
private final AtomicInteger numberOfFileUploadsFailed = new AtomicInteger(); private final AtomicInteger numberOfFileUploadsFailed = new AtomicInteger();
public AtomicInteger getNumberOfFileUploadsFailed() { public AtomicInteger getNumberOfFileUploadsFailed() {
return numberOfFileUploadsFailed; return numberOfFileUploadsFailed;
} }
public AtomicInteger getNumberOfFilesActuallyUpload() { public AtomicInteger getNumberOfFilesActuallyUpload() {
return numberOfFilesActuallyUpload; return numberOfFilesActuallyUpload;
} }
public AtomicInteger getNumberOfFileUploadsExpected() { public AtomicInteger getNumberOfFileUploadsExpected() {
return numberOfFileUploadsExpected; return numberOfFileUploadsExpected;
} }
public List<UploadStatusObject> getUploadedFileStatusList() { public List<UploadStatusObject> getUploadedFileStatusList() {
return uploadedFileStatusList; return uploadedFileStatusList;
} }
public void setUploadedFileStatusList(List<UploadStatusObject> uploadedFileStatusList) { public void setUploadedFileStatusList(final List<UploadStatusObject> uploadedFileStatusList) {
this.uploadedFileStatusList = uploadedFileStatusList; this.uploadedFileStatusList = uploadedFileStatusList;
} }
public boolean isUploadCompleted() { public boolean isUploadCompleted() {
return isUploadCompleted; return uploadCompleted;
} }
public void setUploadCompleted(boolean isUploadCompleted) { public void setUploadCompleted(final boolean uploadCompleted) {
this.isUploadCompleted = isUploadCompleted; this.uploadCompleted = uploadCompleted;
}
public void setStatusPopupMinimized(final boolean statusPopupMinimized) {
this.statusPopupMinimized = statusPopupMinimized;
} }
public void setStatusPopupMinimized(boolean isStatusPopupMinimized) {
this.isStatusPopupMinimized = isStatusPopupMinimized;
}
public boolean isStatusPopupMinimized() { public boolean isStatusPopupMinimized() {
return isStatusPopupMinimized; return statusPopupMinimized;
} }
/**
/**
* Set software. * Set software.
* *
* @return * @return

View File

@@ -8,6 +8,8 @@
*/ */
package org.eclipse.hawkbit.ui.artifacts.upload; package org.eclipse.hawkbit.ui.artifacts.upload;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
/** /**
@@ -16,22 +18,24 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
* popup. * popup.
* *
*/ */
public class UploadStatusObject { public class UploadStatusObject implements Serializable {
private static final long serialVersionUID = 1L;
private String status; private String status;
private Double progress; private Double progress;
private String filename; private String filename;
private String reason; private String reason;
private SoftwareModule selectedSoftwareModule; private final SoftwareModule selectedSoftwareModule;
public UploadStatusObject(final String status, final Double progress, final String fileName, final String reason, public UploadStatusObject(final String status, final Double progress, final String fileName, final String reason,
final SoftwareModule selectedSoftwareModule) { final SoftwareModule selectedSoftwareModule) {
this(fileName,selectedSoftwareModule); this(fileName, selectedSoftwareModule);
this.status = status; this.status = status;
this.progress = progress; this.progress = progress;
this.reason = reason; this.reason = reason;
} }
public UploadStatusObject(String fileName, SoftwareModule selectedSoftwareModule) { public UploadStatusObject(final String fileName, final SoftwareModule selectedSoftwareModule) {
this.filename = fileName; this.filename = fileName;
this.selectedSoftwareModule = selectedSoftwareModule; this.selectedSoftwareModule = selectedSoftwareModule;
} }
@@ -44,7 +48,7 @@ public class UploadStatusObject {
return status; return status;
} }
public void setStatus(String status) { public void setStatus(final String status) {
this.status = status; this.status = status;
} }
@@ -52,7 +56,7 @@ public class UploadStatusObject {
return progress; return progress;
} }
public void setProgress(Double progress) { public void setProgress(final Double progress) {
this.progress = progress; this.progress = progress;
} }
@@ -60,7 +64,7 @@ public class UploadStatusObject {
return filename; return filename;
} }
public void setFilename(String filename) { public void setFilename(final String filename) {
this.filename = filename; this.filename = filename;
} }
@@ -68,19 +72,38 @@ public class UploadStatusObject {
return reason; return reason;
} }
public void setReason(String reason) { public void setReason(final String reason) {
this.reason = reason; this.reason = reason;
} }
@Override @Override
public boolean equals(Object obj) { public int hashCode() {
if (this == null || obj == null) { final int prime = 31;
return false; int result = 1;
} result = prime * result + ((filename == null) ? 0 : filename.hashCode());
if (obj instanceof UploadStatusObject && this.getFilename() == ((UploadStatusObject) obj).getFilename()) { return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true; return true;
} }
return false; if (obj == null) {
return false;
}
if (!(obj instanceof UploadStatusObject)) {
return false;
}
final UploadStatusObject other = (UploadStatusObject) obj;
if (filename == null) {
if (other.filename != null) {
return false;
}
} else if (!filename.equals(other.filename)) {
return false;
}
return true;
} }
} }

View File

@@ -36,6 +36,7 @@ import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button; import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component; import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label; import com.vaadin.ui.Label;
import com.vaadin.ui.Link; import com.vaadin.ui.Link;
@@ -187,6 +188,9 @@ public class CommonDialogWindow extends Window implements Serializable {
((AbstractOrderedLayout) content).setSpacing(true); ((AbstractOrderedLayout) content).setSpacing(true);
((AbstractOrderedLayout) content).setMargin(true); ((AbstractOrderedLayout) content).setMargin(true);
} }
if (content instanceof GridLayout) {
addStyleName("");
}
if (null != content) { if (null != content) {
mainLayout.addComponent(content); mainLayout.addComponent(content);

View File

@@ -175,11 +175,7 @@ public final class UserDetailsFormatter {
private static UserDetails loadUserByUsername(final String username) { private static UserDetails loadUserByUsername(final String username) {
final UserDetailsService userDetailsService = SpringContextHelper.getBean(UserDetailsService.class); final UserDetailsService userDetailsService = SpringContextHelper.getBean(UserDetailsService.class);
try { try {
final UserDetails loadUserByUsername = userDetailsService.loadUserByUsername(username); return userDetailsService.loadUserByUsername(username);
if (loadUserByUsername == null) {
throw new UsernameNotFoundException("User not found " + username);
}
return loadUserByUsername;
} catch (final UsernameNotFoundException e) { } catch (final UsernameNotFoundException e) {
return new User(username, "", Collections.emptyList()); return new User(username, "", Collections.emptyList());
} }

View File

@@ -155,6 +155,7 @@ public abstract class AbstractFilterButtons extends Table {
columnIds.add(FILTER_BUTTON_COLUMN); columnIds.add(FILTER_BUTTON_COLUMN);
setVisibleColumns(columnIds.toArray()); setVisibleColumns(columnIds.toArray());
setColumnHeaderMode(ColumnHeaderMode.HIDDEN); setColumnHeaderMode(ColumnHeaderMode.HIDDEN);
setColumnWidth(FILTER_BUTTON_COLUMN, 137);
} }
private Button createFilterButton(final Long id, final String name, final String description, final String color, private Button createFilterButton(final Long id, final String name, final String description, final String color,
@@ -179,6 +180,7 @@ public abstract class AbstractFilterButtons extends Table {
button.setDescription(name); button.setDescription(name);
} }
button.setData(id == null ? SPUIDefinitions.NO_TAG_BUTTON_ID : itemId); button.setData(id == null ? SPUIDefinitions.NO_TAG_BUTTON_ID : itemId);
return button; return button;
} }

View File

@@ -8,8 +8,6 @@
*/ */
package org.eclipse.hawkbit.ui.decorators; package org.eclipse.hawkbit.ui.decorators;
import org.eclipse.hawkbit.ui.utils.SPUIButtonDefinitions;
import com.vaadin.server.Resource; import com.vaadin.server.Resource;
import com.vaadin.ui.Button; import com.vaadin.ui.Button;
import com.vaadin.ui.themes.ValoTheme; import com.vaadin.ui.themes.ValoTheme;
@@ -22,16 +20,10 @@ public class SPUITagButtonStyle implements SPUIButtonDecorator {
@Override @Override
public Button decorate(final Button button, final String style, final boolean setStyle, final Resource icon) { public Button decorate(final Button button, final String style, final boolean setStyle, final Resource icon) {
/**
* Add ... for long name
*/
final String buttonCaption = button.getCaption();
if (buttonCaption != null && buttonCaption.length() > SPUIButtonDefinitions.BUTTON_CAPTION_LENGTH) {
button.setCaption(buttonCaption.substring(0, SPUIButtonDefinitions.BUTTON_CAPTION_LENGTH) + "...");
}
button.setImmediate(true); button.setImmediate(true);
button.addStyleName("button-no-border" + " " + ValoTheme.BUTTON_BORDERLESS + " " + ValoTheme.BUTTON_TINY + " " button.addStyleName("generatedColumnPadding button-no-border" + " " + ValoTheme.BUTTON_BORDERLESS + " "
+ "button-tag-no-border"); + "button-tag-no-border");
// Set Style // Set Style
if (null != style) { if (null != style) {
if (setStyle) { if (setStyle) {

View File

@@ -717,6 +717,8 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
getColorPickerLayout().getSelPreview().setColor(getColorPickerLayout().getSelectedColor()); getColorPickerLayout().getSelPreview().setColor(getColorPickerLayout().getSelectedColor());
mainLayout.addComponent(colorPickerLayout, 1, 0); mainLayout.addComponent(colorPickerLayout, 1, 0);
mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER); mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER);
} else {
mainLayout.removeComponent(colorPickerLayout);
} }
tagPreviewBtnClicked = !tagPreviewBtnClicked; tagPreviewBtnClicked = !tagPreviewBtnClicked;
} }

Some files were not shown because too many files have changed in this diff Show More