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:
@@ -35,8 +35,6 @@ So we kindly ask contributors:
|
||||
* use [Guava](https://github.com/google/guava) 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:
|
||||
|
||||
* Prefer `Arrays.asList(...)` from JDK over Guava's `Lists.newArrayList(...)`
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-ui</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
|
||||
@@ -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.user.DefaultOidcUser;
|
||||
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.JwtDecoder;
|
||||
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.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
@@ -261,8 +262,11 @@ class JwtAuthoritiesExtractor {
|
||||
Set<GrantedAuthority> extract(final ClientRegistration clientRegistration, final String tokenValue) {
|
||||
try {
|
||||
// Token is already verified by spring security
|
||||
final JwtDecoder jwtDecoder = new NimbusJwtDecoderJwkSupport(
|
||||
clientRegistration.getProviderDetails().getJwkSetUri());
|
||||
final NimbusJwtDecoder jwtDecoder =
|
||||
NimbusJwtDecoder
|
||||
.withJwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri())
|
||||
.jwsAlgorithm(SignatureAlgorithm.from(JwsAlgorithms.RS256))
|
||||
.build();
|
||||
final Jwt token = jwtDecoder.decode(tokenValue);
|
||||
|
||||
return extract(clientRegistration.getClientId(), token.getClaims());
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package org.eclipse.hawkbit.autoconfigure.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -20,14 +19,12 @@ import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
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.mgmt.rest.api.MgmtRestConstants;
|
||||
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.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.ui.MgmtUiConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.ConditionalOnProperty;
|
||||
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.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
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.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.builders.WebSecurity;
|
||||
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.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
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.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.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
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.www.BasicAuthenticationEntryPoint;
|
||||
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.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
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 org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
||||
/**
|
||||
* All configurations related to HawkBit's authentication and authorization
|
||||
@@ -155,18 +130,18 @@ public class SecurityManagedConfiguration {
|
||||
* {@link WebSecurityConfigurer} for the hawkBit server DDI interface.
|
||||
*/
|
||||
@Configuration
|
||||
@Order(300)
|
||||
@EnableWebSecurity
|
||||
@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}/deploymentBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/installedBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING
|
||||
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
@@ -200,9 +175,9 @@ public class SecurityManagedConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosDDiFilter(final HawkbitSecurityProperties securityProperties) {
|
||||
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Arrays.asList(DDI_ANT_MATCHERS),
|
||||
public FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean =
|
||||
dosFilter(List.of(DDI_ANT_MATCHERS),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosDDiFilter");
|
||||
@@ -210,65 +185,69 @@ public class SecurityManagedConfiguration {
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
@Bean
|
||||
@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();
|
||||
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(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
|
||||
tenantAware, systemSecurityContext);
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager());
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager);
|
||||
securityHeaderFilter.setCheckForPrincipalChanges(true);
|
||||
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager());
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
securityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager());
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
HttpSecurity httpSec = http.csrf().disable();
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
|
||||
http
|
||||
.authorizeHttpRequests(amrmRegistry ->
|
||||
amrmRegistry.anyRequest().authenticated())
|
||||
.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()) {
|
||||
|
||||
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()));
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,9 +256,8 @@ public class SecurityManagedConfiguration {
|
||||
* interface.
|
||||
*/
|
||||
@Configuration
|
||||
@Order(301)
|
||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
||||
static class ControllerDownloadSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
static class ControllerDownloadSecurityConfigurationAdapter {
|
||||
|
||||
private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING
|
||||
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*";
|
||||
@@ -316,9 +294,8 @@ public class SecurityManagedConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosDDiDlFilter(final HawkbitSecurityProperties securityProperties) {
|
||||
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Arrays.asList(DDI_DL_ANT_MATCHER),
|
||||
public FilterRegistrationBean<DosFilter> dosFilterDDIDL(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(List.of(DDI_DL_ANT_MATCHER),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosDDiDlFilter");
|
||||
@@ -326,71 +303,75 @@ public class SecurityManagedConfiguration {
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
@Bean
|
||||
@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();
|
||||
|
||||
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(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
|
||||
tenantAware, systemSecurityContext);
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager());
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager);
|
||||
securityHeaderFilter.setCheckForPrincipalChanges(true);
|
||||
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager());
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
securityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager());
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager());
|
||||
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager);
|
||||
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
|
||||
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
HttpSecurity httpSec = http.csrf().disable();
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
|
||||
http
|
||||
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.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()) {
|
||||
|
||||
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()));
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,10 +388,9 @@ public class SecurityManagedConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosSystemFilter(final HawkbitSecurityProperties securityProperties) {
|
||||
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Collections.emptyList(),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(Arrays.asList("/system/*"));
|
||||
filterRegBean.setUrlPatterns(List.of("/system/*"));
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosSystemFilter");
|
||||
|
||||
@@ -420,7 +400,6 @@ public class SecurityManagedConfiguration {
|
||||
private static FilterRegistrationBean<DosFilter> dosFilter(final Collection<String> includeAntPaths,
|
||||
final HawkbitSecurityProperties.Dos.Filter filterProperties,
|
||||
final HawkbitSecurityProperties.Clients clientProperties) {
|
||||
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = new FilterRegistrationBean<>();
|
||||
|
||||
filterRegBean.setFilter(new DosFilter(includeAntPaths, filterProperties.getMaxRead(),
|
||||
@@ -435,36 +414,30 @@ public class SecurityManagedConfiguration {
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@Order(320)
|
||||
@ConditionalOnClass(MgmtApiConfiguration.class)
|
||||
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
public static class IdRestSecurityConfigurationAdapter {
|
||||
|
||||
@Autowired
|
||||
private DdiSecurityProperties ddiSecurityConfiguration;
|
||||
|
||||
@Autowired
|
||||
private DownloadIdCache downloadIdCache;
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
@Bean
|
||||
@Order(320)
|
||||
protected SecurityFilterChain filterChainDLID(
|
||||
final HttpSecurity http,
|
||||
final DdiSecurityProperties ddiSecurityConfiguration, final DownloadIdCache downloadIdCache)
|
||||
throws Exception {
|
||||
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
|
||||
|
||||
final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter(
|
||||
downloadIdCache);
|
||||
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager());
|
||||
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager);
|
||||
|
||||
http.csrf().disable();
|
||||
http.anonymous().disable();
|
||||
http
|
||||
.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,
|
||||
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()));
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,72 +445,79 @@ public class SecurityManagedConfiguration {
|
||||
* Security configuration for the REST management API.
|
||||
*/
|
||||
@Configuration
|
||||
@Order(350)
|
||||
@EnableWebSecurity
|
||||
@ConditionalOnClass(MgmtApiConfiguration.class)
|
||||
public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
public static class RestSecurityConfigurationAdapter {
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private UserAuthenticationFilter userAuthenticationFilter;
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OidcBearerTokenAuthenticationFilter oidcBearerTokenAuthenticationFilter;
|
||||
|
||||
@Autowired(required = false)
|
||||
private InMemoryClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Autowired
|
||||
private SystemManagement systemManagement;
|
||||
|
||||
@Autowired
|
||||
private HawkbitSecurityProperties securityProperties;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
public RestSecurityConfigurationAdapter(final HawkbitSecurityProperties securityProperties) {
|
||||
this.securityProperties = securityProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server Management interface 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.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosMgmtFilter(final HawkbitSecurityProperties securityProperties) {
|
||||
|
||||
public FilterRegistrationBean<DosFilter> dosFilterREST() {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(null,
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(Arrays.asList("/rest/*", "/api/*"));
|
||||
filterRegBean.setUrlPatterns(List.of("/rest/*", "/api/*"));
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosMgmtFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
|
||||
HttpSecurity httpSec = http.requestMatchers().antMatchers("/rest/**", "/system/admin/**").and().csrf()
|
||||
.disable();
|
||||
@Bean
|
||||
@Order(350)
|
||||
protected SecurityFilterChain filterChainREST(
|
||||
final HttpSecurity http,
|
||||
@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()) {
|
||||
httpSec = httpSec.cors().configurationSource(reuest -> corsConfiguration()).and();
|
||||
http.cors(configurer -> configurer.configurationSource(corsConfigurationSource()));
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
// Only get the first client registration. Testing against every
|
||||
// client could increase the
|
||||
// attack vector
|
||||
@@ -547,16 +527,16 @@ public class SecurityManagedConfiguration {
|
||||
: null;
|
||||
|
||||
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);
|
||||
|
||||
httpSec.addFilterAfter(oidcBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class);
|
||||
http.addFilterAfter(oidcBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class);
|
||||
} else {
|
||||
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm());
|
||||
|
||||
httpSec.addFilterBefore(new Filter() {
|
||||
http.addFilterBefore(new Filter() {
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
userAuthenticationFilter.init(filterConfig);
|
||||
@@ -573,20 +553,22 @@ public class SecurityManagedConfiguration {
|
||||
userAuthenticationFilter.destroy();
|
||||
}
|
||||
}, RequestHeaderAuthenticationFilter.class);
|
||||
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint);
|
||||
http
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(basicAuthEntryPoint));
|
||||
}
|
||||
|
||||
httpSec.addFilterAfter(
|
||||
new AuthenticationSuccessTenantMetadataCreationFilter(systemManagement, systemSecurityContext),
|
||||
SessionManagementFilter.class);
|
||||
|
||||
httpSec.anonymous().disable();
|
||||
httpSec.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.cors", name = "enabled", matchIfMissing = false)
|
||||
CorsConfiguration corsConfiguration() {
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.cors", name = "enabled")
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
final CorsConfiguration configuration = corsConfiguration();
|
||||
return request -> configuration;
|
||||
}
|
||||
|
||||
private CorsConfiguration corsConfiguration() {
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
corsConfiguration.setAllowedOrigins(securityProperties.getCors().getAllowedOrigins());
|
||||
@@ -599,281 +581,15 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link WebSecurityConfigurer} for external (management) access.
|
||||
*/
|
||||
@Configuration
|
||||
@Order(400)
|
||||
@EnableWebSecurity
|
||||
@EnableVaadinSharedSecurity
|
||||
@ConditionalOnClass(MgmtUiConfiguration.class)
|
||||
public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@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);
|
||||
private static AuthenticationManager setAuthenticationManager(final HttpSecurity http, final DdiSecurityProperties ddiSecurityConfiguration) throws Exception {
|
||||
// configure authentication manager
|
||||
final AuthenticationManager authenticationManager =
|
||||
http
|
||||
.getSharedObject(AuthenticationManagerBuilder.class)
|
||||
.authenticationProvider(
|
||||
new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()))
|
||||
.build();
|
||||
http.authenticationManager(authenticationManager);
|
||||
return authenticationManager;
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ org.eclipse.hawkbit.autoconfigure.cache.CacheAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.cache.DownloadIdCacheAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.ddi.DDiApiAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.dmf.amqp.DmfApiAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.mgmt.ui.MgmtUiAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.mgmt.MgmtApiAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.repository.event.EventPublisherAutoConfiguration,\
|
||||
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.InMemoryUserManagementAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.security.OidcUserManagementAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.web.WebMvcAutoConfiguration,\
|
||||
org.eclipse.hawkbit.autoconfigure.PropertyHostnameResolverAutoConfiguration
|
||||
|
||||
@@ -61,11 +61,6 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit-test</artifactId>
|
||||
|
||||
@@ -29,8 +29,6 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -62,13 +60,14 @@ public class AmqpTestConfiguration {
|
||||
return new ThreadPoolTaskScheduler();
|
||||
}
|
||||
|
||||
@SuppressWarnings("java:S112")
|
||||
@Bean
|
||||
HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) {
|
||||
return () -> {
|
||||
try {
|
||||
return new URL(serverProperties.getUrl());
|
||||
} catch (final MalformedURLException e) {
|
||||
throw Throwables.propagate(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.rabbitmq.test;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
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.ConnectionFactory;
|
||||
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.domain.UserPermissions;
|
||||
|
||||
@@ -39,9 +39,9 @@ public class RabbitMqSetupService {
|
||||
|
||||
private final String hostname;
|
||||
|
||||
private String username;
|
||||
private final String username;
|
||||
|
||||
private String password;
|
||||
private final String password;
|
||||
|
||||
public RabbitMqSetupService() {
|
||||
|
||||
@@ -52,12 +52,13 @@ public class RabbitMqSetupService {
|
||||
password = brokerSupport.getPassword();
|
||||
}
|
||||
|
||||
@SuppressWarnings("java:S112")
|
||||
private synchronized Client getRabbitmqHttpClient() {
|
||||
if (rabbitmqHttpClient == null) {
|
||||
try {
|
||||
rabbitmqHttpClient = new Client(getHttpApiUrl(), getUsername(), getPassword());
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw Throwables.propagate(e);
|
||||
rabbitmqHttpClient = new Client(new URL(getHttpApiUrl()), getUsername(), getPassword());
|
||||
} catch (final MalformedURLException | URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return rabbitmqHttpClient;
|
||||
@@ -77,7 +78,7 @@ public class RabbitMqSetupService {
|
||||
|
||||
@PreDestroy
|
||||
public void deleteVirtualHost() {
|
||||
if (StringUtils.isEmpty(virtualHost)) {
|
||||
if (ObjectUtils.isEmpty(virtualHost)) {
|
||||
return;
|
||||
}
|
||||
getRabbitmqHttpClient().deleteVhost(virtualHost);
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.Optional;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -30,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@NoRepositoryBean
|
||||
@Transactional(readOnly = true)
|
||||
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.
|
||||
|
||||
@@ -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.model.DistributionSetMetadata;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(readOnly = true)
|
||||
public interface DistributionSetMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
|
||||
CrudRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaDistributionSetMetadata> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,8 @@ import org.springframework.data.repository.query.Param;
|
||||
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)
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -28,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(readOnly = true)
|
||||
public interface SoftwareModuleMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||
CrudRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.model.TargetMetadata;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(readOnly = true)
|
||||
public interface TargetMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
|
||||
CrudRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaTargetMetadata> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,8 @@ import org.springframework.lang.NonNull;
|
||||
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)
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -23,7 +24,9 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
*
|
||||
*/
|
||||
@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.
|
||||
|
||||
@@ -42,7 +42,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@ContextConfiguration(classes = { RepositoryApplicationConfiguration.class, TestConfiguration.class,
|
||||
@ContextConfiguration(classes = {
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class,
|
||||
TestSupportBinderAutoConfiguration.class })
|
||||
@TestPropertySource(locations = "classpath:/jpa-test.properties")
|
||||
public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@@ -54,7 +54,8 @@ import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
|
||||
import com.fasterxml.jackson.dataformat.cbor.CBORParser;
|
||||
|
||||
@ContextConfiguration(classes = { DdiApiConfiguration.class, RestConfiguration.class,
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class, TestSupportBinderAutoConfiguration.class })
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class,
|
||||
TestSupportBinderAutoConfiguration.class })
|
||||
@TestPropertySource(locations = "classpath:/ddi-test.properties")
|
||||
public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest {
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
|
||||
@ContextConfiguration(classes = { MgmtApiConfiguration.class, RestConfiguration.class,
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class, TestSupportBinderAutoConfiguration.class })
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class,
|
||||
TestSupportBinderAutoConfiguration.class })
|
||||
@TestPropertySource(locations = "classpath:/mgmt-test.properties")
|
||||
public abstract class AbstractManagementApiIntegrationTest extends AbstractRestIntegrationTest {
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ import io.qameta.allure.Feature;
|
||||
@Feature("Documentation Verification - API")
|
||||
@ExtendWith(RestDocumentationExtension.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" })
|
||||
public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrationTest {
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.test.context.support.WithUserDetails;
|
||||
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.Story;
|
||||
|
||||
@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false", "hawkbit.server.security.cors.enabled=true",
|
||||
"hawkbit.server.security.cors.allowedOrigins=" + CorsTest.ALLOWED_ORIGIN_FIRST + ","
|
||||
@SpringBootTest(properties = {
|
||||
"hawkbit.dmf.rabbitmq.enabled=false",
|
||||
"hawkbit.server.security.cors.enabled=true",
|
||||
"hawkbit.server.security.cors.allowedOrigins="
|
||||
+ CorsTest.ALLOWED_ORIGIN_FIRST + ","
|
||||
+ 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")
|
||||
public class CorsTest extends AbstractSecurityTest {
|
||||
|
||||
|
||||
@@ -149,6 +149,16 @@
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</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>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* which accompanies this distribution, and is available at
|
||||
* 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;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* which accompanies this distribution, and is available at
|
||||
* 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.web.bind.annotation.GetMapping;
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
hawkbit-ui/src/main/resources/META-INF/spring.factories
Normal file
3
hawkbit-ui/src/main/resources/META-INF/spring.factories
Normal file
@@ -0,0 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.eclipse.hawkbit.ui.autoconfigure.MgmtUiAutoConfiguration
|
||||
18
pom.xml
18
pom.xml
@@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.12</version>
|
||||
<version>2.7.13</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
@@ -127,16 +127,14 @@
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<snapshotDependencyAllowed>true</snapshotDependencyAllowed>
|
||||
|
||||
<java.version>17</java.version>
|
||||
|
||||
<spring.boot.version>2.7.12</spring.boot.version>
|
||||
<spring-framework.version>5.3.26</spring-framework.version>
|
||||
<spring.boot.version>2.7.13</spring.boot.version>
|
||||
<spring.cloud.version>2021.0.5</spring.cloud.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
|
||||
upgrade) - START -->
|
||||
<!-- Newer versions needed than defined in Boot -->
|
||||
@@ -148,11 +146,6 @@
|
||||
<!-- CVE-2020-36518, CVE-2022-42003, CVE-2022-42004 -->
|
||||
<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 -->
|
||||
|
||||
<rabbitmq.http-client.version>3.12.1</rabbitmq.http-client.version>
|
||||
@@ -853,11 +846,6 @@
|
||||
<artifactId>spring-plugin-core</artifactId>
|
||||
<version>${spring.plugin.core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-client</artifactId>
|
||||
<version>${spring-security-oauth2-client.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Protostuff Io -->
|
||||
<dependency>
|
||||
|
||||
Reference in New Issue
Block a user