[#1383] Spring Boot 3 Migration / Step 1 (#1384)

1. PagingAndSortingRepository doesn't extend CrudRepository anymore. For all extending that interface repositories CrudRepository super interface shall be now declared (https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0 -
```
The popular PagingAndSortingRepository used to extend from CrudRepository, but it no longer does. This lets you combine it
with either CrudRepository or ListCrudRepository or a base interface of your own creation. This means you now have to
explicitly extend from a CRUD fragment, even when you already extend from PagingAndSortingRepository.
```
)
2. org.eclipse.hawkbit.autoconfigure.mgmt.ui -> move in hawkbit-ui (to be ready for removal), anyway - it's a better location for ui related configs
3. extends WebMvcConfigurerAdapter -> implements WebMvcConfigurer
4. remove WebSecurityConfigurerAdapter -> https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html#_stop_using_websecurityconfigureradapter, https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
and add @Order to the bean reg!!
5. Use configurers (the other will be deprecated / removed), e.d:  http.csrf().disable() -> http.csrf(AbstractHttpConfigurer::disable)
6. configure(final AuthenticationManagerBuilder auth) -> put in httpsecurity config - http.getSharedObject(AuthenticationManagerBuilder.class).... (https://www.baeldung.com/spring-security-authentication-provider)
7. configure(final WebSecurity webSecurity) ->
```
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**");
}
```
(https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter)
8. AuthenticationManager authenticationManagerBean() ->
```
@Bean
AuthenticationManager authenticationManager(final AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}
```
(https://backendstory.com/spring-security-how-to-replace-websecurityconfigureradapter/)
9. WebMvcAutoConfiguration could be removed - it uses deprectated methods, and sets properties that are same by default - hence - not neeeded
(https://github.com/spring-projects/spring-framework/issues/23915#issuecomment-563987147)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2023-07-17 10:36:26 +03:00
committed by GitHub
parent bcc2616e73
commit 56ea5b15c9
27 changed files with 582 additions and 583 deletions

View File

@@ -35,8 +35,6 @@ So we kindly ask contributors:
* use [Guava](https://github.com/google/guava) if feasible * use [Guava](https://github.com/google/guava) if feasible
* use [Apache commons lang](https://commons.apache.org/proper/commons-lang/) if feasible * use [Apache commons lang](https://commons.apache.org/proper/commons-lang/) if feasible
Note that the guava project for instance often documents where they think that JDK is having a similar functionality (e.g. their thoughts on [Throwables.propagate](https://github.com/google/guava/wiki/Why-we-deprecated-Throwables.propagate)).
Examples: Examples:
* Prefer `Arrays.asList(...)` from JDK over Guava's `Lists.newArrayList(...)` * Prefer `Arrays.asList(...)` from JDK over Guava's `Lists.newArrayList(...)`

View File

@@ -31,12 +31,6 @@
<version>${project.version}</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ui</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.hawkbit</groupId> <groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-jpa</artifactId> <artifactId>hawkbit-repository-jpa</artifactId>

View File

@@ -53,10 +53,11 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo; import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.security.oauth2.jwt.JwtException;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
@@ -261,8 +262,11 @@ class JwtAuthoritiesExtractor {
Set<GrantedAuthority> extract(final ClientRegistration clientRegistration, final String tokenValue) { Set<GrantedAuthority> extract(final ClientRegistration clientRegistration, final String tokenValue) {
try { try {
// Token is already verified by spring security // Token is already verified by spring security
final JwtDecoder jwtDecoder = new NimbusJwtDecoderJwkSupport( final NimbusJwtDecoder jwtDecoder =
clientRegistration.getProviderDetails().getJwkSetUri()); NimbusJwtDecoder
.withJwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri())
.jwsAlgorithm(SignatureAlgorithm.from(JwsAlgorithms.RS256))
.build();
final Jwt token = jwtDecoder.decode(tokenValue); final Jwt token = jwtDecoder.decode(tokenValue);
return extract(clientRegistration.getClientId(), token.getClaims()); return extract(clientRegistration.getClientId(), token.getClaims());

View File

@@ -9,7 +9,6 @@
package org.eclipse.hawkbit.autoconfigure.security; package org.eclipse.hawkbit.autoconfigure.security;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -20,14 +19,12 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration; import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter; import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration; import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
@@ -46,7 +43,6 @@ import org.eclipse.hawkbit.security.HttpDownloadAuthenticationFilter;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
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.eclipse.hawkbit.ui.MgmtUiConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -54,62 +50,41 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.config.Customizer;
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.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.method.configuration.GlobalMethodSecurityConfiguration;
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;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
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.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.session.SessionManagementFilter; import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.vaadin.spring.http.HttpService; import org.springframework.web.cors.CorsConfigurationSource;
import org.vaadin.spring.security.annotation.EnableVaadinSharedSecurity;
import org.vaadin.spring.security.config.VaadinSharedSecurityConfiguration;
import org.vaadin.spring.security.shared.VaadinAuthenticationSuccessHandler;
import org.vaadin.spring.security.shared.VaadinUrlAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.VaadinRedirectStrategy;
/** /**
* All configurations related to HawkBit's authentication and authorization * All configurations related to HawkBit's authentication and authorization
@@ -155,18 +130,18 @@ public class SecurityManagedConfiguration {
* {@link WebSecurityConfigurer} for the hawkBit server DDI interface. * {@link WebSecurityConfigurer} for the hawkBit server DDI interface.
*/ */
@Configuration @Configuration
@Order(300) @EnableWebSecurity
@ConditionalOnClass(DdiApiConfiguration.class) @ConditionalOnClass(DdiApiConfiguration.class)
static class ControllerSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { static class ControllerSecurityConfigurationAdapter {
private static final String[] DDI_ANT_MATCHERS = { DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}", private static final String[] DDI_ANT_MATCHERS = {
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/confirmationBase/**", DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/confirmationBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/deploymentBase/**", DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/deploymentBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/installedBase/**", DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/installedBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**", DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData", DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData",
DdiRestConstants.BASE_V1_REQUEST_MAPPING DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
private final ControllerManagement controllerManagement; private final ControllerManagement controllerManagement;
private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantConfigurationManagement tenantConfigurationManagement;
@@ -200,9 +175,9 @@ public class SecurityManagedConfiguration {
*/ */
@Bean @Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosDDiFilter(final HawkbitSecurityProperties securityProperties) { public FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean =
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Arrays.asList(DDI_ANT_MATCHERS), dosFilter(List.of(DDI_ANT_MATCHERS),
securityProperties.getDos().getFilter(), securityProperties.getClients()); securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setOrder(DOS_FILTER_ORDER); filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosDDiFilter"); filterRegBean.setName("dosDDiFilter");
@@ -210,65 +185,69 @@ public class SecurityManagedConfiguration {
return filterRegBean; return filterRegBean;
} }
@Override @Bean
protected void configure(final HttpSecurity http) throws Exception { @Order(300)
protected SecurityFilterChain filterChainDDI(final HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
http
.requestMatchers(requestMatchers -> requestMatchers.antMatchers(DDI_ANT_MATCHERS))
.csrf(AbstractHttpConfigurer::disable);
if (securityProperties.isRequireSsl()) {
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
}
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource(); final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info(
"""
******************
** Anonymous controller security enabled, should only be used for developing purposes **
******************""");
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
List.of(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
http
.securityContext(AbstractHttpConfigurer::disable)
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
} else {
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter( final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
ddiSecurityConfiguration.getRp().getCnHeader(), ddiSecurityConfiguration.getRp().getCnHeader(),
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement, ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
tenantAware, systemSecurityContext); tenantAware, systemSecurityContext);
securityHeaderFilter.setAuthenticationManager(authenticationManager()); securityHeaderFilter.setAuthenticationManager(authenticationManager);
securityHeaderFilter.setCheckForPrincipalChanges(true); securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource); securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter( final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext); tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
securityTokenFilter.setAuthenticationManager(authenticationManager()); securityTokenFilter.setAuthenticationManager(authenticationManager);
securityTokenFilter.setCheckForPrincipalChanges(true); securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource); securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter( final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext); tenantConfigurationManagement, tenantAware, systemSecurityContext);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager()); gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true); gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource); gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
HttpSecurity httpSec = http.csrf().disable(); http
.authorizeHttpRequests(amrmRegistry ->
if (securityProperties.isRequireSsl()) { amrmRegistry.anyRequest().authenticated())
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); .anonymous(AbstractHttpConfigurer::disable)
.addFilter(securityHeaderFilter)
.addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter)
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
(request, response, authException) ->
response.setStatus(HttpStatus.UNAUTHORIZED.value())))
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
} }
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) { return http.build();
LOG.info(
"******************\n** Anonymous controller security enabled, should only be used for developing purposes **\n******************");
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers(DDI_ANT_MATCHERS).and().securityContext().disable().anonymous()
.authenticationFilter(anonymousFilter);
} else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).requestMatchers().antMatchers(DDI_ANT_MATCHERS).and()
.anonymous().disable().authorizeRequests().anyRequest().authenticated().and()
.exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response
.setStatus(HttpStatus.UNAUTHORIZED.value()))
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }
@@ -277,9 +256,8 @@ public class SecurityManagedConfiguration {
* interface. * interface.
*/ */
@Configuration @Configuration
@Order(301)
@ConditionalOnClass(DdiApiConfiguration.class) @ConditionalOnClass(DdiApiConfiguration.class)
static class ControllerDownloadSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { static class ControllerDownloadSecurityConfigurationAdapter {
private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*"; + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*";
@@ -316,9 +294,8 @@ public class SecurityManagedConfiguration {
*/ */
@Bean @Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosDDiDlFilter(final HawkbitSecurityProperties securityProperties) { public FilterRegistrationBean<DosFilter> dosFilterDDIDL(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(List.of(DDI_DL_ANT_MATCHER),
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Arrays.asList(DDI_DL_ANT_MATCHER),
securityProperties.getDos().getFilter(), securityProperties.getClients()); securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setOrder(DOS_FILTER_ORDER); filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosDDiDlFilter"); filterRegBean.setName("dosDDiDlFilter");
@@ -326,71 +303,75 @@ public class SecurityManagedConfiguration {
return filterRegBean; return filterRegBean;
} }
@Override @Bean
protected void configure(final HttpSecurity http) throws Exception { @Order(301)
protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
http
.requestMatcher(new AntPathRequestMatcher(DDI_DL_ANT_MATCHER))
.csrf(AbstractHttpConfigurer::disable);
if (securityProperties.isRequireSsl()) {
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
}
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource(); final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info(
"""
******************
** Anonymous controller security enabled, should only be used for developing purposes **
******************""");
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
List.of(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
http
.securityContext(AbstractHttpConfigurer::disable)
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
} else {
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter( final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
ddiSecurityConfiguration.getRp().getCnHeader(), ddiSecurityConfiguration.getRp().getCnHeader(),
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement, ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
tenantAware, systemSecurityContext); tenantAware, systemSecurityContext);
securityHeaderFilter.setAuthenticationManager(authenticationManager()); securityHeaderFilter.setAuthenticationManager(authenticationManager);
securityHeaderFilter.setCheckForPrincipalChanges(true); securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource); securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter( final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext); tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
securityTokenFilter.setAuthenticationManager(authenticationManager()); securityTokenFilter.setAuthenticationManager(authenticationManager);
securityTokenFilter.setCheckForPrincipalChanges(true); securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource); securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter( final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext); tenantConfigurationManagement, tenantAware, systemSecurityContext);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager()); gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true); gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource); gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter( final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext); tenantConfigurationManagement, tenantAware, systemSecurityContext);
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager()); controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager);
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true); controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource); controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
HttpSecurity httpSec = http.csrf().disable(); http
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
if (securityProperties.isRequireSsl()) { .anonymous(AbstractHttpConfigurer::disable)
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); .addFilter(securityHeaderFilter)
.addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter)
.addFilter(controllerAnonymousDownloadFilter)
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
(request, response, authException) -> response.setStatus(HttpStatus.UNAUTHORIZED.value())))
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
} }
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) { return http.build();
LOG.info(
"******************\n** Anonymous controller security enabled, should only be used for developing purposes **\n******************");
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers(DDI_DL_ANT_MATCHER).and().securityContext().disable().anonymous()
.authenticationFilter(anonymousFilter);
} else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
.requestMatchers().antMatchers(DDI_DL_ANT_MATCHER).and().anonymous().disable()
.authorizeRequests().anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response
.setStatus(HttpStatus.UNAUTHORIZED.value()))
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }
@@ -407,10 +388,9 @@ public class SecurityManagedConfiguration {
@Bean @Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosSystemFilter(final HawkbitSecurityProperties securityProperties) { public FilterRegistrationBean<DosFilter> dosSystemFilter(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Collections.emptyList(), final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Collections.emptyList(),
securityProperties.getDos().getFilter(), securityProperties.getClients()); securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setUrlPatterns(Arrays.asList("/system/*")); filterRegBean.setUrlPatterns(List.of("/system/*"));
filterRegBean.setOrder(DOS_FILTER_ORDER); filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosSystemFilter"); filterRegBean.setName("dosSystemFilter");
@@ -420,7 +400,6 @@ public class SecurityManagedConfiguration {
private static FilterRegistrationBean<DosFilter> dosFilter(final Collection<String> includeAntPaths, private static FilterRegistrationBean<DosFilter> dosFilter(final Collection<String> includeAntPaths,
final HawkbitSecurityProperties.Dos.Filter filterProperties, final HawkbitSecurityProperties.Dos.Filter filterProperties,
final HawkbitSecurityProperties.Clients clientProperties) { final HawkbitSecurityProperties.Clients clientProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean = new FilterRegistrationBean<>(); final FilterRegistrationBean<DosFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new DosFilter(includeAntPaths, filterProperties.getMaxRead(), filterRegBean.setFilter(new DosFilter(includeAntPaths, filterProperties.getMaxRead(),
@@ -435,36 +414,30 @@ public class SecurityManagedConfiguration {
*/ */
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@Order(320)
@ConditionalOnClass(MgmtApiConfiguration.class) @ConditionalOnClass(MgmtApiConfiguration.class)
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { public static class IdRestSecurityConfigurationAdapter {
@Autowired @Bean
private DdiSecurityProperties ddiSecurityConfiguration; @Order(320)
protected SecurityFilterChain filterChainDLID(
@Autowired final HttpSecurity http,
private DownloadIdCache downloadIdCache; final DdiSecurityProperties ddiSecurityConfiguration, final DownloadIdCache downloadIdCache)
throws Exception {
@Override final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
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);
http.csrf().disable(); http
http.anonymous().disable(); .requestMatcher(new AntPathRequestMatcher("/**/downloadId/**"))
.authorizeHttpRequests(armrRepository -> armrRepository.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.anonymous(AbstractHttpConfigurer::disable)
.addFilterBefore(downloadIdAuthenticationFilter, FilterSecurityInterceptor.class)
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.antMatcher("/**/downloadId/**").addFilterBefore(downloadIdAuthenticationFilter, return http.build();
FilterSecurityInterceptor.class);
http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }
@@ -472,72 +445,79 @@ public class SecurityManagedConfiguration {
* Security configuration for the REST management API. * Security configuration for the REST management API.
*/ */
@Configuration @Configuration
@Order(350)
@EnableWebSecurity @EnableWebSecurity
@ConditionalOnClass(MgmtApiConfiguration.class) @ConditionalOnClass(MgmtApiConfiguration.class)
public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { public static class RestSecurityConfigurationAdapter {
@Autowired private final HawkbitSecurityProperties securityProperties;
@Lazy
private UserAuthenticationFilter userAuthenticationFilter;
@Autowired(required = false) public RestSecurityConfigurationAdapter(final HawkbitSecurityProperties securityProperties) {
private OidcBearerTokenAuthenticationFilter oidcBearerTokenAuthenticationFilter; this.securityProperties = securityProperties;
}
@Autowired(required = false)
private InMemoryClientRegistrationRepository clientRegistrationRepository;
@Autowired
private SystemManagement systemManagement;
@Autowired
private HawkbitSecurityProperties securityProperties;
@Autowired
private SystemSecurityContext systemSecurityContext;
/** /**
* Filter to protect the hawkBit server Management interface against to * Filter to protect the hawkBit server Management interface against to
* many requests. * many requests.
* *
* @param securityProperties
* for filter configuration
*
* @return the spring filter registration bean for registering a denial * @return the spring filter registration bean for registering a denial
* of service protection filter in the filter chain * of service protection filter in the filter chain
*/ */
@Bean @Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosMgmtFilter(final HawkbitSecurityProperties securityProperties) { public FilterRegistrationBean<DosFilter> dosFilterREST() {
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(null, final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(null,
securityProperties.getDos().getFilter(), securityProperties.getClients()); securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setUrlPatterns(Arrays.asList("/rest/*", "/api/*")); filterRegBean.setUrlPatterns(List.of("/rest/*", "/api/*"));
filterRegBean.setOrder(DOS_FILTER_ORDER); filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosMgmtFilter"); filterRegBean.setName("dosMgmtFilter");
return filterRegBean; return filterRegBean;
} }
@Override @Bean
protected void configure(final HttpSecurity http) throws Exception { @Order(350)
protected SecurityFilterChain filterChainREST(
HttpSecurity httpSec = http.requestMatchers().antMatchers("/rest/**", "/system/admin/**").and().csrf() final HttpSecurity http,
.disable(); @Lazy
final UserAuthenticationFilter userAuthenticationFilter,
@Autowired(required = false)
final OidcBearerTokenAuthenticationFilter oidcBearerTokenAuthenticationFilter,
@Autowired(required = false)
final InMemoryClientRegistrationRepository clientRegistrationRepository,
final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext)
throws Exception {
http
.requestMatchers(requestMatchers -> requestMatchers.antMatchers("/rest/**", MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**"))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(amrmRegistry ->
amrmRegistry
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
.anyRequest()
.authenticated())
.addFilterAfter(
// Servlet filter to create metadata after successful authentication over RESTful.
(request, response, chain) -> {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata);
}
chain.doFilter(request, response);
},
SessionManagementFilter.class)
.anonymous(AbstractHttpConfigurer::disable)
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
if (securityProperties.getCors().isEnabled()) { if (securityProperties.getCors().isEnabled()) {
httpSec = httpSec.cors().configurationSource(reuest -> corsConfiguration()).and(); http.cors(configurer -> configurer.configurationSource(corsConfigurationSource()));
} }
if (securityProperties.isRequireSsl()) { if (securityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
} }
httpSec.authorizeRequests().antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN).anyRequest().authenticated();
if (oidcBearerTokenAuthenticationFilter != null) { if (oidcBearerTokenAuthenticationFilter != null) {
// Only get the first client registration. Testing against every // Only get the first client registration. Testing against every
// client could increase the // client could increase the
// attack vector // attack vector
@@ -547,16 +527,16 @@ public class SecurityManagedConfiguration {
: null; : null;
Assert.notNull(clientRegistration, "There must be a valid client registration"); Assert.notNull(clientRegistration, "There must be a valid client registration");
httpSec.oauth2ResourceServer().jwt().jwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri()); http.oauth2ResourceServer(configurer -> configurer.jwt().jwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri()));
oidcBearerTokenAuthenticationFilter.setClientRegistration(clientRegistration); oidcBearerTokenAuthenticationFilter.setClientRegistration(clientRegistration);
httpSec.addFilterAfter(oidcBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class); http.addFilterAfter(oidcBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class);
} else { } else {
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint(); final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm()); basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm());
httpSec.addFilterBefore(new Filter() { http.addFilterBefore(new Filter() {
@Override @Override
public void init(final FilterConfig filterConfig) throws ServletException { public void init(final FilterConfig filterConfig) throws ServletException {
userAuthenticationFilter.init(filterConfig); userAuthenticationFilter.init(filterConfig);
@@ -573,20 +553,22 @@ public class SecurityManagedConfiguration {
userAuthenticationFilter.destroy(); userAuthenticationFilter.destroy();
} }
}, RequestHeaderAuthenticationFilter.class); }, RequestHeaderAuthenticationFilter.class);
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint); http
.httpBasic(Customizer.withDefaults())
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(basicAuthEntryPoint));
} }
httpSec.addFilterAfter( return http.build();
new AuthenticationSuccessTenantMetadataCreationFilter(systemManagement, systemSecurityContext),
SessionManagementFilter.class);
httpSec.anonymous().disable();
httpSec.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
} }
@Bean @Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.cors", name = "enabled", matchIfMissing = false) @ConditionalOnProperty(prefix = "hawkbit.server.security.cors", name = "enabled")
CorsConfiguration corsConfiguration() { CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = corsConfiguration();
return request -> configuration;
}
private CorsConfiguration corsConfiguration() {
final CorsConfiguration corsConfiguration = new CorsConfiguration(); final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(securityProperties.getCors().getAllowedOrigins()); corsConfiguration.setAllowedOrigins(securityProperties.getCors().getAllowedOrigins());
@@ -599,281 +581,15 @@ public class SecurityManagedConfiguration {
} }
} }
/** private static AuthenticationManager setAuthenticationManager(final HttpSecurity http, final DdiSecurityProperties ddiSecurityConfiguration) throws Exception {
* {@link WebSecurityConfigurer} for external (management) access. // configure authentication manager
*/ final AuthenticationManager authenticationManager =
@Configuration http
@Order(400) .getSharedObject(AuthenticationManagerBuilder.class)
@EnableWebSecurity .authenticationProvider(
@EnableVaadinSharedSecurity new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()))
@ConditionalOnClass(MgmtUiConfiguration.class) .build();
public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { http.authenticationManager(authenticationManager);
return authenticationManager;
@Autowired
private HawkbitSecurityProperties hawkbitSecurityProperties;
@Autowired(required = false)
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
@Autowired(required = false)
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private LogoutHandler logoutHandler;
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
/**
* Filter to protect the hawkBit management UI against to many requests.
*
* @param securityProperties
* for filter configuration
*
* @return the spring filter registration bean for registering a denial
* of service protection filter in the filter chain
*/
@Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.ui-filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosMgmtUiFilter(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(null,
securityProperties.getDos().getUiFilter(), securityProperties.getClients());
// All URLs that can be called anonymous
filterRegBean.setUrlPatterns(Arrays.asList("/UI/login", "/UI/login/*", "/UI/logout", "/UI/logout/*"));
filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosMgmtUiFilter");
return filterRegBean;
}
@Override
@Bean(name = VaadinSharedSecurityConfiguration.AUTHENTICATION_MANAGER_BEAN)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* Overwriting VaadinAuthenticationSuccessHandler of default
* VaadinSharedSecurityConfiguration
*
* @return the vaadin success authentication handler
*/
@Primary
@Bean(name = VaadinSharedSecurityConfiguration.VAADIN_AUTHENTICATION_SUCCESS_HANDLER_BEAN)
public VaadinAuthenticationSuccessHandler redirectSaveHandler(final HttpService httpService,
final VaadinRedirectStrategy redirectStrategy) {
final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(
httpService, redirectStrategy, "/UI/");
handler.setTargetUrlParameter("r");
return handler;
}
/**
* Listener to redirect to login page after session timeout. Close the
* vaadin session, because it's is not possible to redirect in
* atmosphere.
*
* @return the servlet listener.
*/
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
final boolean enableOidc = oidcUserService != null && authenticationSuccessHandler != null;
// workaround regex: we need to exclude the URL /UI/HEARTBEAT here
// because we bound the vaadin application to /UI and not to root,
// described in vaadin-forum:
// https://vaadin.com/forum#!/thread/3200565.
HttpSecurity httpSec;
if (enableOidc) {
httpSec = http.requestMatchers().antMatchers("/**/UI/**", "/**/oauth2/**").and();
} else {
httpSec = http.antMatcher("/**/UI/**");
}
// disable as CSRF is handled by Vaadin
httpSec.csrf().disable();
// allow same origin X-Frame-Options for correct file download under
// Safari
httpSec.headers().frameOptions().sameOrigin();
if (hawkbitSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} else {
LOG.info(
"\"******************\\n** Requires HTTPS Security has been disabled for UI, should only be used for developing purposes **\\n******************\"");
}
if (!StringUtils.isEmpty(hawkbitSecurityProperties.getContentSecurityPolicy())) {
httpSec.headers().contentSecurityPolicy(hawkbitSecurityProperties.getContentSecurityPolicy());
}
// UI
httpSec.authorizeRequests().antMatchers("/UI/login/**", "/UI/UIDL/**").permitAll().anyRequest()
.authenticated();
if (enableOidc) {
// OIDC
httpSec.oauth2Login().userInfoEndpoint().oidcUserService(oidcUserService).and()
.successHandler(authenticationSuccessHandler).and().oauth2Client();
} else {
// UI login / Basic auth
httpSec.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/UI/login"));
}
// UI logout
httpSec.logout().logoutUrl("/UI/logout*").addLogoutHandler(logoutHandler)
.logoutSuccessHandler(logoutSuccessHandler);
}
/**
* HttpFirewall which enables to define a list of allowed host names.
*
* @return the http firewall.
*/
@Bean
public HttpFirewall httpFirewall() {
final List<String> allowedHostNames = hawkbitSecurityProperties.getAllowedHostNames();
final IgnorePathsStrictHttpFirewall firewall = new IgnorePathsStrictHttpFirewall(
hawkbitSecurityProperties.getHttpFirewallIgnoredPaths());
if (!CollectionUtils.isEmpty(allowedHostNames)) {
firewall.setAllowedHostnames(hostName -> {
LOG.debug("Firewall check host: {}, allowed: {}", hostName, allowedHostNames.contains(hostName));
return allowedHostNames.contains(hostName);
});
}
return firewall;
}
private static class IgnorePathsStrictHttpFirewall extends StrictHttpFirewall {
private final Collection<String> pathsToIgnore;
public IgnorePathsStrictHttpFirewall(final Collection<String> pathsToIgnore) {
super();
this.pathsToIgnore = pathsToIgnore;
}
@Override
public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) {
if (pathsToIgnore != null && pathsToIgnore.contains(request.getRequestURI())) {
return new FirewalledRequest(request) {
@Override
public void reset() {
// nothing to do
}
};
}
return super.getFirewalledRequest(request);
}
}
@Override
public void configure(final WebSecurity webSecurity) throws Exception {
// No security for static content
webSecurity.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**");
}
/**
* Configuration that defines the {@link AccessDecisionManager} bean for
* UI method security used by the Vaadin Servlet. Notice: we can not use
* the top-level method security configuration because
* {@link AdviceMode.ASPECTJ} is not supported.
*/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
@ConditionalOnClass(MgmtUiConfiguration.class)
static class UIMethodSecurity extends GlobalMethodSecurityConfiguration {
@Bean(name = VaadinSharedSecurityConfiguration.ACCESS_DECISION_MANAGER_BEAN)
@Override
protected AccessDecisionManager accessDecisionManager() {
return super.accessDecisionManager();
}
}
} }
} }
/**
* After a successful login on the UI we need to ensure to create the tenant
* meta data within SP.
*/
class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler {
@Autowired
private SystemManagement systemManagement;
@Autowired
private SystemSecurityContext systemSecurityContext;
public TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(final HttpService http,
final VaadinRedirectStrategy redirectStrategy, final String defaultTargetUrl) {
super(http, redirectStrategy, defaultTargetUrl);
}
@Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, getTenantFrom(authentication));
super.onAuthenticationSuccess(authentication);
}
private static String getTenantFrom(final Authentication authentication) {
final Object details = authentication.getDetails();
if (details instanceof TenantAwareAuthenticationDetails) {
return ((TenantAwareAuthenticationDetails) details).getTenant();
}
throw new InsufficientAuthenticationException("Authentication details/tenant info are not specified!");
}
}
/**
* Servletfilter to create metadata after successful authentication over
* RESTful.
*/
class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
private final SystemManagement systemManagement;
private final SystemSecurityContext systemSecurityContext;
AuthenticationSuccessTenantMetadataCreationFilter(final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext) {
this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext;
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
// not needed
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
lazyCreateTenantMetadata();
chain.doFilter(request, response);
}
private void lazyCreateTenantMetadata() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata);
}
}
@Override
public void destroy() {
// not needed
}
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.web;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* A configuration bean which disables the {@code useSuffixPatternMatch} feature
* from Spring because it will truncate the dot in a REST URL which leads to
* problem in case a controllerId contains dots and is a path parameter or
* filename ending.
*/
@Configuration
public class WebMvcAutoConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(final PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
configurer.setUseRegisteredSuffixPatternMatch(false);
}
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}

View File

@@ -4,7 +4,6 @@ org.eclipse.hawkbit.autoconfigure.cache.CacheAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.DownloadIdCacheAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.cache.DownloadIdCacheAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.ddi.DDiApiAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.ddi.DDiApiAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.dmf.amqp.DmfApiAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.dmf.amqp.DmfApiAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.mgmt.ui.MgmtUiAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.mgmt.MgmtApiAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.mgmt.MgmtApiAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.repository.event.EventPublisherAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.repository.event.EventPublisherAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.repository.ArtifactFilesystemAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.repository.ArtifactFilesystemAutoConfiguration,\
@@ -14,5 +13,4 @@ org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.security.OidcUserManagementAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.security.OidcUserManagementAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.web.WebMvcAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.PropertyHostnameResolverAutoConfiguration org.eclipse.hawkbit.autoconfigure.PropertyHostnameResolverAutoConfiguration

View File

@@ -61,11 +61,6 @@
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.amqp</groupId> <groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId> <artifactId>spring-rabbit-test</artifactId>

View File

@@ -29,8 +29,6 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService; import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
import com.google.common.base.Throwables;
/** /**
* *
*/ */
@@ -62,13 +60,14 @@ public class AmqpTestConfiguration {
return new ThreadPoolTaskScheduler(); return new ThreadPoolTaskScheduler();
} }
@SuppressWarnings("java:S112")
@Bean @Bean
HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) { HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) {
return () -> { return () -> {
try { try {
return new URL(serverProperties.getUrl()); return new URL(serverProperties.getUrl());
} catch (final MalformedURLException e) { } catch (final MalformedURLException e) {
throw Throwables.propagate(e); throw new RuntimeException(e);
} }
}; };
} }

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.rabbitmq.test;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import java.util.UUID; import java.util.UUID;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@@ -17,9 +18,8 @@ import javax.annotation.PreDestroy;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; 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.junit.BrokerRunningSupport; import org.springframework.amqp.rabbit.junit.BrokerRunningSupport;
import org.springframework.util.StringUtils; import org.springframework.util.ObjectUtils;
import com.google.common.base.Throwables;
import com.rabbitmq.http.client.Client; import com.rabbitmq.http.client.Client;
import com.rabbitmq.http.client.domain.UserPermissions; import com.rabbitmq.http.client.domain.UserPermissions;
@@ -39,9 +39,9 @@ public class RabbitMqSetupService {
private final String hostname; private final String hostname;
private String username; private final String username;
private String password; private final String password;
public RabbitMqSetupService() { public RabbitMqSetupService() {
@@ -52,12 +52,13 @@ public class RabbitMqSetupService {
password = brokerSupport.getPassword(); password = brokerSupport.getPassword();
} }
@SuppressWarnings("java:S112")
private synchronized Client getRabbitmqHttpClient() { private synchronized Client getRabbitmqHttpClient() {
if (rabbitmqHttpClient == null) { if (rabbitmqHttpClient == null) {
try { try {
rabbitmqHttpClient = new Client(getHttpApiUrl(), getUsername(), getPassword()); rabbitmqHttpClient = new Client(new URL(getHttpApiUrl()), getUsername(), getPassword());
} catch (MalformedURLException | URISyntaxException e) { } catch (final MalformedURLException | URISyntaxException e) {
throw Throwables.propagate(e); throw new RuntimeException(e);
} }
} }
return rabbitmqHttpClient; return rabbitmqHttpClient;
@@ -77,7 +78,7 @@ public class RabbitMqSetupService {
@PreDestroy @PreDestroy
public void deleteVirtualHost() { public void deleteVirtualHost() {
if (StringUtils.isEmpty(virtualHost)) { if (ObjectUtils.isEmpty(virtualHost)) {
return; return;
} }
getRabbitmqHttpClient().deleteVhost(virtualHost); getRabbitmqHttpClient().deleteVhost(virtualHost);

View File

@@ -15,6 +15,7 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity; import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
import org.eclipse.hawkbit.repository.model.BaseEntity; import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -30,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
@NoRepositoryBean @NoRepositoryBean
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity, I extends Serializable> public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity, I extends Serializable>
extends PagingAndSortingRepository<T, I>, NoCountSliceRepository<T> { extends PagingAndSortingRepository<T, I>, CrudRepository<T, I>, NoCountSliceRepository<T> {
/** /**
* Retrieves an {@link BaseEntity} by its id. * Retrieves an {@link BaseEntity} by its id.

View File

@@ -12,6 +12,7 @@ import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface DistributionSetMetadataRepository public interface DistributionSetMetadataRepository
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>, extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
CrudRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
JpaSpecificationExecutor<JpaDistributionSetMetadata> { JpaSpecificationExecutor<JpaDistributionSetMetadata> {
/** /**

View File

@@ -27,7 +27,8 @@ import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link PagingAndSortingRepository} for {@link DistributionSetType}. * {@link PagingAndSortingRepository} and {@link org.springframework.data.repository.CrudRepository} for
* {@link DistributionSetType}.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)

View File

@@ -17,6 +17,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -28,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface SoftwareModuleMetadataRepository public interface SoftwareModuleMetadataRepository
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>, extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
CrudRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> { JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
/** /**

View File

@@ -12,6 +12,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.model.TargetMetadata; import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface TargetMetadataRepository public interface TargetMetadataRepository
extends PagingAndSortingRepository<JpaTargetMetadata, TargetMetadataCompositeKey>, extends PagingAndSortingRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
CrudRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
JpaSpecificationExecutor<JpaTargetMetadata> { JpaSpecificationExecutor<JpaTargetMetadata> {
/** /**

View File

@@ -30,7 +30,8 @@ import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link PagingAndSortingRepository} for {@link JpaTargetType}. * {@link PagingAndSortingRepository} and {@link org.springframework.data.repository.CrudRepository} for
* {@link JpaTargetType}.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)

View File

@@ -14,6 +14,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -23,7 +24,9 @@ import org.springframework.transaction.annotation.Transactional;
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface TenantMetaDataRepository extends PagingAndSortingRepository<JpaTenantMetaData, Long> { public interface TenantMetaDataRepository
extends PagingAndSortingRepository<JpaTenantMetaData, Long>,
CrudRepository<JpaTenantMetaData, Long> {
/** /**
* Search {@link TenantMetaData} by tenant name. * Search {@link TenantMetaData} by tenant name.

View File

@@ -42,7 +42,8 @@ import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@ContextConfiguration(classes = { RepositoryApplicationConfiguration.class, TestConfiguration.class, @ContextConfiguration(classes = {
RepositoryApplicationConfiguration.class, TestConfiguration.class,
TestSupportBinderAutoConfiguration.class }) TestSupportBinderAutoConfiguration.class })
@TestPropertySource(locations = "classpath:/jpa-test.properties") @TestPropertySource(locations = "classpath:/jpa-test.properties")
public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest { public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest {

View File

@@ -54,7 +54,8 @@ import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
import com.fasterxml.jackson.dataformat.cbor.CBORParser; import com.fasterxml.jackson.dataformat.cbor.CBORParser;
@ContextConfiguration(classes = { DdiApiConfiguration.class, RestConfiguration.class, @ContextConfiguration(classes = { DdiApiConfiguration.class, RestConfiguration.class,
RepositoryApplicationConfiguration.class, TestConfiguration.class, TestSupportBinderAutoConfiguration.class }) RepositoryApplicationConfiguration.class, TestConfiguration.class,
TestSupportBinderAutoConfiguration.class })
@TestPropertySource(locations = "classpath:/ddi-test.properties") @TestPropertySource(locations = "classpath:/ddi-test.properties")
public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest { public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest {

View File

@@ -30,7 +30,8 @@ import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.ResultMatcher;
@ContextConfiguration(classes = { MgmtApiConfiguration.class, RestConfiguration.class, @ContextConfiguration(classes = { MgmtApiConfiguration.class, RestConfiguration.class,
RepositoryApplicationConfiguration.class, TestConfiguration.class, TestSupportBinderAutoConfiguration.class }) RepositoryApplicationConfiguration.class, TestConfiguration.class,
TestSupportBinderAutoConfiguration.class })
@TestPropertySource(locations = "classpath:/mgmt-test.properties") @TestPropertySource(locations = "classpath:/mgmt-test.properties")
public abstract class AbstractManagementApiIntegrationTest extends AbstractRestIntegrationTest { public abstract class AbstractManagementApiIntegrationTest extends AbstractRestIntegrationTest {

View File

@@ -71,7 +71,8 @@ import io.qameta.allure.Feature;
@Feature("Documentation Verification - API") @Feature("Documentation Verification - API")
@ExtendWith(RestDocumentationExtension.class) @ExtendWith(RestDocumentationExtension.class)
@ContextConfiguration(classes = { DdiApiConfiguration.class, MgmtApiConfiguration.class, RestConfiguration.class, @ContextConfiguration(classes = { DdiApiConfiguration.class, MgmtApiConfiguration.class, RestConfiguration.class,
RepositoryApplicationConfiguration.class, TestConfiguration.class, TestSupportBinderAutoConfiguration.class }) RepositoryApplicationConfiguration.class, TestConfiguration.class,
TestSupportBinderAutoConfiguration.class })
@TestPropertySource(locations = { "classpath:/updateserver-restdocumentation-test.properties" }) @TestPropertySource(locations = { "classpath:/updateserver-restdocumentation-test.properties" })
public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrationTest { public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrationTest {

View File

@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultActions;
@@ -25,10 +26,15 @@ import io.qameta.allure.Description;
import io.qameta.allure.Feature; import io.qameta.allure.Feature;
import io.qameta.allure.Story; import io.qameta.allure.Story;
@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false", "hawkbit.server.security.cors.enabled=true", @SpringBootTest(properties = {
"hawkbit.server.security.cors.allowedOrigins=" + CorsTest.ALLOWED_ORIGIN_FIRST + "," "hawkbit.dmf.rabbitmq.enabled=false",
"hawkbit.server.security.cors.enabled=true",
"hawkbit.server.security.cors.allowedOrigins="
+ CorsTest.ALLOWED_ORIGIN_FIRST + ","
+ CorsTest.ALLOWED_ORIGIN_SECOND, + CorsTest.ALLOWED_ORIGIN_SECOND,
"hawkbit.server.security.cors.exposedHeaders=Access-Control-Allow-Origin" })@Feature("Integration Test - Security") "hawkbit.server.security.cors.exposedHeaders="
+ HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN})
@Feature("Integration Test - Security")
@Story("CORS") @Story("CORS")
public class CorsTest extends AbstractSecurityTest { public class CorsTest extends AbstractSecurityTest {

View File

@@ -149,6 +149,16 @@
<artifactId>hawkbit-repository-api</artifactId> <artifactId>hawkbit-repository-api</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-http-security</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>

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.autoconfigure.mgmt.ui; package org.eclipse.hawkbit.ui.autoconfigure;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;

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.autoconfigure.mgmt.ui; package org.eclipse.hawkbit.ui.autoconfigure;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;

View File

@@ -0,0 +1,306 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.autoconfigure;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.DosFilter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.ui.MgmtUiConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InsufficientAuthenticationException;
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.GlobalMethodSecurityConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.vaadin.spring.http.HttpService;
import org.vaadin.spring.security.annotation.EnableVaadinSharedSecurity;
import org.vaadin.spring.security.config.VaadinSharedSecurityConfiguration;
import org.vaadin.spring.security.shared.VaadinAuthenticationSuccessHandler;
import org.vaadin.spring.security.shared.VaadinUrlAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.VaadinRedirectStrategy;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* {@link WebSecurityConfigurer} for external (management) access.
*/
@Configuration
@EnableWebSecurity
@EnableVaadinSharedSecurity
@ConditionalOnClass(MgmtUiConfiguration.class)
public class UISecurityConfigurationAdapter {
private static final Logger LOG = LoggerFactory.getLogger(UISecurityConfigurationAdapter.class);
private static final int DOS_FILTER_ORDER = -200;
@Autowired
private HawkbitSecurityProperties hawkbitSecurityProperties;
/**
* Filter to protect the hawkBit management UI against to many requests.
*
* @param securityProperties for filter configuration
* @return the spring filter registration bean for registering a denial
* of service protection filter in the filter chain
*/
@Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.ui-filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosMgmtUiFilter(final HawkbitSecurityProperties securityProperties) {
final HawkbitSecurityProperties.Dos.Filter filterProperties = securityProperties.getDos().getUiFilter();
final HawkbitSecurityProperties.Clients clientProperties = securityProperties.getClients();
final FilterRegistrationBean<DosFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new DosFilter(null, filterProperties.getMaxRead(),
filterProperties.getMaxWrite(), filterProperties.getWhitelist(), clientProperties.getBlacklist(),
clientProperties.getRemoteIpHeader()));
// All URLs that can be called anonymous
filterRegBean.setUrlPatterns(Arrays.asList("/UI/login", "/UI/login/*", "/UI/logout", "/UI/logout/*"));
filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosMgmtUiFilter");
return filterRegBean;
}
@Bean
AuthenticationManager authenticationManager(final AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
/**
* Overwriting VaadinAuthenticationSuccessHandler of default
* VaadinSharedSecurityConfiguration
*
* @return the vaadin success authentication handler
*/
@Primary
@Bean(name = VaadinSharedSecurityConfiguration.VAADIN_AUTHENTICATION_SUCCESS_HANDLER_BEAN)
public VaadinAuthenticationSuccessHandler redirectSaveHandler(final HttpService httpService,
final VaadinRedirectStrategy redirectStrategy) {
final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(
httpService, redirectStrategy, "/UI/");
handler.setTargetUrlParameter("r");
return handler;
}
/**
* Listener to redirect to login page after session timeout. Close the
* vaadin session, because it's is not possible to redirect in
* atmosphere.
*
* @return the servlet listener.
*/
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
}
@Bean
@Order(400)
protected SecurityFilterChain filterChainUI(
final HttpSecurity http,
@Autowired(required = false)
final OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService,
@Autowired(required = false)
final AuthenticationSuccessHandler authenticationSuccessHandler,
final LogoutHandler logoutHandler,
final LogoutSuccessHandler logoutSuccessHandler)
throws Exception {
final boolean enableOidc = oidcUserService != null && authenticationSuccessHandler != null;
// workaround regex: we need to exclude the URL /UI/HEARTBEAT here
// because we bound the vaadin application to /UI and not to root,
// described in vaadin-forum:
// https://vaadin.com/forum#!/thread/3200565.
HttpSecurity httpSec;
if (enableOidc) {
httpSec = http.requestMatchers().antMatchers("/**/UI/**", "/**/oauth2/**").and();
} else {
httpSec = http.antMatcher("/**/UI/**");
}
// disable as CSRF is handled by Vaadin
httpSec.csrf(AbstractHttpConfigurer::disable);
// allow same origin X-Frame-Options for correct file download under
// Safari
httpSec.headers().frameOptions().sameOrigin();
if (hawkbitSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
} else {
LOG.info(
"""
******************
** Requires HTTPS Security has been disabled for UI, should only be used for developing purposes **
******************""");
}
if (!ObjectUtils.isEmpty(hawkbitSecurityProperties.getContentSecurityPolicy())) {
httpSec.headers().contentSecurityPolicy(hawkbitSecurityProperties.getContentSecurityPolicy());
}
// UI
httpSec.authorizeRequests().antMatchers("/UI/login/**", "/UI/UIDL/**").permitAll().anyRequest()
.authenticated();
if (enableOidc) {
// OIDC
httpSec.oauth2Login().userInfoEndpoint().oidcUserService(oidcUserService).and()
.successHandler(authenticationSuccessHandler).and().oauth2Client();
} else {
// UI login / Basic auth
httpSec.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/UI/login"));
}
// UI logout
httpSec.logout().logoutUrl("/UI/logout*").addLogoutHandler(logoutHandler)
.logoutSuccessHandler(logoutSuccessHandler);
return httpSec.build();
}
/**
* HttpFirewall which enables to define a list of allowed host names.
*
* @return the http firewall.
*/
@Bean
public HttpFirewall httpFirewall() {
final List<String> allowedHostNames = hawkbitSecurityProperties.getAllowedHostNames();
final IgnorePathsStrictHttpFirewall firewall = new IgnorePathsStrictHttpFirewall(
hawkbitSecurityProperties.getHttpFirewallIgnoredPaths());
if (!CollectionUtils.isEmpty(allowedHostNames)) {
firewall.setAllowedHostnames(hostName -> {
LOG.debug("Firewall check host: {}, allowed: {}", hostName, allowedHostNames.contains(hostName));
return allowedHostNames.contains(hostName);
});
}
return firewall;
}
private static class IgnorePathsStrictHttpFirewall extends StrictHttpFirewall {
private final Collection<String> pathsToIgnore;
public IgnorePathsStrictHttpFirewall(final Collection<String> pathsToIgnore) {
super();
this.pathsToIgnore = pathsToIgnore;
}
@Override
public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) {
if (pathsToIgnore != null && pathsToIgnore.contains(request.getRequestURI())) {
return new FirewalledRequest(request) {
@Override
public void reset() {
// nothing to do
}
};
}
return super.getFirewalledRequest(request);
}
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
// No security for static content
return (web) -> web.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**");
}
/**
* Configuration that defines the {@link AccessDecisionManager} bean for
* UI method security used by the Vaadin Servlet. Notice: we can not use
* the top-level method security configuration because
* AdviceMode.ASPECTJ is not supported.
*/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
@ConditionalOnClass(MgmtUiConfiguration.class)
static class UIMethodSecurity extends GlobalMethodSecurityConfiguration {
@Bean(name = VaadinSharedSecurityConfiguration.ACCESS_DECISION_MANAGER_BEAN)
@Override
protected AccessDecisionManager accessDecisionManager() {
return super.accessDecisionManager();
}
}
/**
* After a successful login on the UI we need to ensure to create the tenant
* meta data within SP.
*/
class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler {
@Autowired
private SystemManagement systemManagement;
@Autowired
private SystemSecurityContext systemSecurityContext;
public TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(final HttpService http,
final VaadinRedirectStrategy redirectStrategy, final String defaultTargetUrl) {
super(http, redirectStrategy, defaultTargetUrl);
}
@Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, getTenantFrom(authentication));
super.onAuthenticationSuccess(authentication);
}
private static String getTenantFrom(final Authentication authentication) {
final Object details = authentication.getDetails();
if (details instanceof TenantAwareAuthenticationDetails) {
return ((TenantAwareAuthenticationDetails) details).getTenant();
}
throw new InsufficientAuthenticationException("Authentication details/tenant info are not specified!");
}
}
}

View File

@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.eclipse.hawkbit.ui.autoconfigure.MgmtUiAutoConfiguration

18
pom.xml
View File

@@ -16,7 +16,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version> <version>2.7.13</version>
</parent> </parent>
<groupId>org.eclipse.hawkbit</groupId> <groupId>org.eclipse.hawkbit</groupId>
@@ -127,16 +127,14 @@
</repositories> </repositories>
<properties> <properties>
<snapshotDependencyAllowed>true</snapshotDependencyAllowed>
<java.version>17</java.version> <java.version>17</java.version>
<spring.boot.version>2.7.12</spring.boot.version> <spring.boot.version>2.7.13</spring.boot.version>
<spring-framework.version>5.3.26</spring-framework.version>
<spring.cloud.version>2021.0.5</spring.cloud.version> <spring.cloud.version>2021.0.5</spring.cloud.version>
<spring.plugin.core.version>2.0.0.RELEASE</spring.plugin.core.version> <spring.plugin.core.version>2.0.0.RELEASE</spring.plugin.core.version>
<snapshotDependencyAllowed>true</snapshotDependencyAllowed>
<!-- Spring boot version overrides (should be reviewed with every boot <!-- Spring boot version overrides (should be reviewed with every boot
upgrade) - START --> upgrade) - START -->
<!-- Newer versions needed than defined in Boot --> <!-- Newer versions needed than defined in Boot -->
@@ -148,11 +146,6 @@
<!-- CVE-2020-36518, CVE-2022-42003, CVE-2022-42004 --> <!-- CVE-2020-36518, CVE-2022-42003, CVE-2022-42004 -->
<jackson-bom.version>2.14.2</jackson-bom.version> <jackson-bom.version>2.14.2</jackson-bom.version>
<!-- SONATYPE-2021-1175 -->
<logback.version>1.2.9</logback.version>
<!-- CVE-2022-31690 -->
<spring-security-oauth2-client.version>5.7.7</spring-security-oauth2-client.version>
<!-- Spring boot version overrides - END --> <!-- Spring boot version overrides - END -->
<rabbitmq.http-client.version>3.12.1</rabbitmq.http-client.version> <rabbitmq.http-client.version>3.12.1</rabbitmq.http-client.version>
@@ -853,11 +846,6 @@
<artifactId>spring-plugin-core</artifactId> <artifactId>spring-plugin-core</artifactId>
<version>${spring.plugin.core.version}</version> <version>${spring.plugin.core.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>${spring-security-oauth2-client.version}</version>
</dependency>
<!-- Protostuff Io --> <!-- Protostuff Io -->
<dependency> <dependency>