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 [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(...)`
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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.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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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>
|
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user