Support for simultaneous base and OAuth authentication (#1785)
* Remove _OidcAuthenticationSuccessHandler_: * _OAuth2AuthenticationToken.setDetails_ is made by jwt authentication converter * get tenant data (with potentially creating tenant) is done via a filter added in filterChainREST * _filterChainREST_ uses _Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>>_ as configuration for OAuth. Thus it is not bound with oauth client configuration * _OidcUserManagementAutoConfiguration_ - now registers (if conditions are met) Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> which covers both - oauth legacy filter from filterChainREST and OidcBearerTokenAuthenticationFilter * Since oauth clients are not related to hawkBit anymore (since removal of legacy UI) and the proper configuration would be via resource server or whatever, the _OidcUserManagementAutoConfiguration_ is DEPRECATED and for removal * _UserAuthenticationFilter_ is removed * Enabled sumiltaneous base and oauth authentication. Still, by default, if OAuth configured http authentication is disabled. However, if OAuth it is configured (via _Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>>)_ and **hawkbit.server.security.allowHttpBasicOnOAuthEnabled** is set to **true** then http auth would be also enabled * _OidcUserManagementAutoConfiguration_ could be disabled with **hawkbit.server.security.oAuth2OnClientsConfig.enabled=false** Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -9,42 +9,32 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
@@ -56,31 +46,24 @@ 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.JwtDecoders;
|
||||
import org.springframework.security.oauth2.jwt.JwtException;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Auto-configuration for OpenID Connect user management.
|
||||
* Auto-configuration for OpenID Connect user management. Based on clients configuration.
|
||||
*
|
||||
* @deprecated hawkBit doesn't use/depend on clients configuration (it was back in time of integrated UI.
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(value = ClientsConfiguredCondition.class)
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.oAuth2OnClientsConfig", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@Deprecated(forRemoval = true)
|
||||
public class OidcUserManagementAutoConfiguration {
|
||||
|
||||
/**
|
||||
* @return the OpenID Connect authentication success handler
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationSuccessHandler oidcAuthenticationSuccessHandler(
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext) {
|
||||
return new OidcAuthenticationSuccessHandler(systemManagement, systemSecurityContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a jwt authorities extractor which interprets the roles of a user
|
||||
* as their authorities.
|
||||
@@ -105,16 +88,43 @@ public class OidcUserManagementAutoConfiguration {
|
||||
return new JwtAuthoritiesOidcUserService(extractor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an authentication filter for using OAuth2 Bearer Tokens.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
OidcBearerTokenAuthenticationFilter oidcBearerTokenAuthenticationFilter(
|
||||
final JwtAuthoritiesExtractor authoritiesExtractor,
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext) {
|
||||
return new OidcBearerTokenAuthenticationFilter(
|
||||
authoritiesExtractor, systemManagement, systemSecurityContext);
|
||||
Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> oauth2ResourceServerCustomizer(
|
||||
final InMemoryClientRegistrationRepository clientRegistrationRepository,
|
||||
final JwtAuthoritiesExtractor authoritiesExtractor) {
|
||||
// Only get the first client registration. Testing against every client could increase the attack vector (?)
|
||||
final ClientRegistration clientRegistration =
|
||||
clientRegistrationRepository.iterator().hasNext() ? clientRegistrationRepository.iterator().next() : null;
|
||||
Assert.notNull(clientRegistration, "There must be a valid client registration");
|
||||
|
||||
return configurer -> configurer.jwt(configurer2 -> {
|
||||
if (clientRegistration.getProviderDetails().getJwkSetUri() == null) {
|
||||
configurer2.decoder(JwtDecoders.fromIssuerLocation(clientRegistration.getProviderDetails().getIssuerUri()));
|
||||
} else {
|
||||
configurer2.jwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri());
|
||||
}
|
||||
configurer2.jwtAuthenticationConverter(jwt -> {
|
||||
final String defaultTenant = "DEFAULT";
|
||||
|
||||
final OidcIdToken idToken = new OidcIdToken(
|
||||
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());
|
||||
final OidcUserInfo userInfo = new OidcUserInfo(jwt.getClaims());
|
||||
|
||||
final Set<GrantedAuthority> authorities = authoritiesExtractor.extract(jwt, clientRegistration);
|
||||
if (authorities.isEmpty()) {
|
||||
throw new AccessDeniedException("No authorities found in token");
|
||||
}
|
||||
|
||||
final DefaultOidcUser user = new DefaultOidcUser(authorities, idToken, userInfo);
|
||||
|
||||
final OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(
|
||||
user, authorities, clientRegistration.getRegistrationId());
|
||||
|
||||
oAuth2AuthenticationToken.setDetails(new TenantAwareAuthenticationDetails(defaultTenant, false));
|
||||
return oAuth2AuthenticationToken;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,42 +176,11 @@ public class OidcUserManagementAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenID Connect Authentication Success Handler which load tenant data
|
||||
*/
|
||||
private static class OidcAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
OidcAuthenticationSuccessHandler(
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext) {
|
||||
this.systemManagement = systemManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
final HttpServletRequest request, final HttpServletResponse response,
|
||||
final Authentication authentication) throws ServletException, IOException {
|
||||
if (authentication instanceof AbstractAuthenticationToken token) {
|
||||
final String defaultTenant = "DEFAULT";
|
||||
|
||||
token.setDetails(new TenantAwareAuthenticationDetails(defaultTenant, false));
|
||||
|
||||
systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant);
|
||||
}
|
||||
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to extract authorities out of the jwt. It interprets the user's
|
||||
* role as their authorities.
|
||||
*/
|
||||
private record DefaultJwtAuthoritiesExtractor
|
||||
(GrantedAuthoritiesMapper authoritiesMapper) implements JwtAuthoritiesExtractor {
|
||||
private record DefaultJwtAuthoritiesExtractor(GrantedAuthoritiesMapper authoritiesMapper) implements JwtAuthoritiesExtractor {
|
||||
|
||||
private static final OAuth2Error INVALID_REQUEST = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
|
||||
|
||||
@@ -240,68 +219,4 @@ public class OidcUserManagementAutoConfiguration {
|
||||
return new LinkedHashSet<>(authorities);
|
||||
}
|
||||
}
|
||||
|
||||
static class OidcBearerTokenAuthenticationFilter implements UserAuthenticationFilter, Filter {
|
||||
|
||||
private final JwtAuthoritiesExtractor authoritiesExtractor;
|
||||
private final SystemManagement systemManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private ClientRegistration clientRegistration;
|
||||
|
||||
OidcBearerTokenAuthenticationFilter(
|
||||
final JwtAuthoritiesExtractor authoritiesExtractor,
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext) {
|
||||
this.authoritiesExtractor = authoritiesExtractor;
|
||||
this.systemManagement = systemManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
void setClientRegistration(final ClientRegistration clientRegistration) {
|
||||
this.clientRegistration = clientRegistration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication instanceof JwtAuthenticationToken jwtAuthenticationToken) {
|
||||
final String defaultTenant = "DEFAULT";
|
||||
|
||||
final Jwt jwt = jwtAuthenticationToken.getToken();
|
||||
final OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),
|
||||
jwt.getClaims());
|
||||
final OidcUserInfo userInfo = new OidcUserInfo(jwt.getClaims());
|
||||
|
||||
final Set<GrantedAuthority> authorities = authoritiesExtractor.extract(jwt, clientRegistration);
|
||||
|
||||
if (authorities.isEmpty()) {
|
||||
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
final DefaultOidcUser user = new DefaultOidcUser(authorities, idToken, userInfo);
|
||||
|
||||
final OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(user, authorities,
|
||||
clientRegistration.getRegistrationId());
|
||||
|
||||
oAuth2AuthenticationToken.setDetails(new TenantAwareAuthenticationDetails(defaultTenant, false));
|
||||
|
||||
systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant);
|
||||
SecurityContextHolder.getContext().setAuthentication(oAuth2AuthenticationToken);
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -28,7 +21,6 @@ import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
@@ -48,13 +40,11 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -62,31 +52,23 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.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.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoders;
|
||||
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.AuthorizationFilter;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.firewall.FirewalledRequest;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
@@ -104,31 +86,13 @@ import org.springframework.web.cors.CorsConfigurationSource;
|
||||
public class SecurityManagedConfiguration {
|
||||
|
||||
private static final int DOS_FILTER_ORDER = -200;
|
||||
public static final String ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES = """
|
||||
******************
|
||||
** Anonymous controller security enabled, should only be used for development purposes **
|
||||
******************""";
|
||||
|
||||
/**
|
||||
* @return the {@link UserAuthenticationFilter} to include into the hawkBit security configuration.
|
||||
* @throws Exception lazy bean exception maybe if the authentication manager cannot be instantiated
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
// Exception squid:S00112 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S00112" })
|
||||
UserAuthenticationFilter userAuthenticationFilter(final AuthenticationConfiguration configuration)
|
||||
throws Exception {
|
||||
return new UserAuthenticationFilterBasicAuth(configuration.getAuthenticationManager());
|
||||
}
|
||||
|
||||
private static final class UserAuthenticationFilterBasicAuth extends BasicAuthenticationFilter
|
||||
implements UserAuthenticationFilter {
|
||||
|
||||
private UserAuthenticationFilterBasicAuth(final AuthenticationManager authenticationManager) {
|
||||
super(authenticationManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link WebSecurityConfigurer} for the hawkBit server DDI interface.
|
||||
* Security configuration for the hawkBit server DDI interface.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -165,18 +129,14 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server DDI interface against to many
|
||||
* requests.
|
||||
* Filter to protect the hawkBit server DDI interface against too 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
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
|
||||
protected FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean =
|
||||
dosFilter(List.of(DDI_ANT_MATCHERS),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
@@ -201,11 +161,7 @@ public class SecurityManagedConfiguration {
|
||||
|
||||
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
|
||||
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
|
||||
log.info(
|
||||
"""
|
||||
******************
|
||||
** Anonymous controller security enabled, should only be used for developing purposes **
|
||||
******************""");
|
||||
log.warn(ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
|
||||
|
||||
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
|
||||
"controllerAnonymousFilter", "anonymous",
|
||||
@@ -253,8 +209,7 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link WebSecurityConfigurer} for the hawkBit server DDI download
|
||||
* interface.
|
||||
* Security configuration for the hawkBit server DDI download interface.
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
||||
@@ -284,14 +239,10 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server DDI download interface against
|
||||
* to many requests.
|
||||
* Filter to protect the hawkBit server DDI download interface against too 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
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
@@ -320,11 +271,7 @@ public class SecurityManagedConfiguration {
|
||||
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
|
||||
|
||||
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
|
||||
log.info(
|
||||
"""
|
||||
******************
|
||||
** Anonymous controller security enabled, should only be used for developing purposes **
|
||||
******************""");
|
||||
log.warn(ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
|
||||
|
||||
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
|
||||
"controllerAnonymousFilter", "anonymous",
|
||||
@@ -377,14 +324,10 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server system management interface against
|
||||
* to many requests.
|
||||
* Filter to protect the hawkBit server system management interface against too 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
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
@@ -411,7 +354,7 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* A Websecurity config to handle and filter the download ids.
|
||||
* Security config to handle and filter the download ids.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -435,7 +378,7 @@ public class SecurityManagedConfiguration {
|
||||
.authorizeHttpRequests(armrRepository -> armrRepository.anyRequest().authenticated())
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.addFilterBefore(downloadIdAuthenticationFilter, FilterSecurityInterceptor.class)
|
||||
.addFilterBefore(downloadIdAuthenticationFilter, AuthorizationFilter.class)
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
|
||||
return http.build();
|
||||
@@ -460,15 +403,17 @@ public class SecurityManagedConfiguration {
|
||||
* Filter to protect the hawkBit server Management interface against to
|
||||
* many requests.
|
||||
*
|
||||
* @return the spring filter registration bean for registering a denial
|
||||
* of service protection filter in the filter chain
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosFilterREST() {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(null,
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(List.of("/rest/*", "/api/*"));
|
||||
filterRegBean.setUrlPatterns(List.of(
|
||||
MgmtRestConstants.BASE_REST_MAPPING + "/*",
|
||||
MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/*",
|
||||
MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE + "/*"));
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosMgmtFilter");
|
||||
|
||||
@@ -479,25 +424,22 @@ public class SecurityManagedConfiguration {
|
||||
@Order(350)
|
||||
SecurityFilterChain filterChainREST(
|
||||
final HttpSecurity http,
|
||||
@Lazy
|
||||
final UserAuthenticationFilter userAuthenticationFilter,
|
||||
@Autowired(required = false)
|
||||
final OidcUserManagementAutoConfiguration.OidcBearerTokenAuthenticationFilter
|
||||
oidcBearerTokenAuthenticationFilter,
|
||||
@Autowired(required = false)
|
||||
final InMemoryClientRegistrationRepository clientRegistrationRepository,
|
||||
final Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> oauth2ResourceServerCustomizer,
|
||||
final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext)
|
||||
throws Exception {
|
||||
final SystemSecurityContext systemSecurityContext) throws Exception {
|
||||
http
|
||||
.securityMatcher("/rest/**", MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.securityMatcher(MgmtRestConstants.BASE_REST_MAPPING + "/**", MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.authorizeHttpRequests(amrmRegistry ->
|
||||
amrmRegistry
|
||||
.requestMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.exceptionHandling(Customizer.withDefaults())
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.addFilterAfter(
|
||||
// Servlet filter to create metadata after successful authentication over RESTful.
|
||||
(request, response, chain) -> {
|
||||
@@ -507,9 +449,7 @@ public class SecurityManagedConfiguration {
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
},
|
||||
SessionManagementFilter.class)
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
SessionManagementFilter.class);
|
||||
|
||||
if (securityProperties.getCors().isEnabled()) {
|
||||
http.cors(configurer -> configurer.configurationSource(corsConfigurationSource()));
|
||||
@@ -519,64 +459,21 @@ public class SecurityManagedConfiguration {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
if (oidcBearerTokenAuthenticationFilter != null) {
|
||||
// Only get the first client registration. Testing against every
|
||||
// client could increase the
|
||||
// attack vector
|
||||
final ClientRegistration clientRegistration = clientRegistrationRepository != null
|
||||
&& clientRegistrationRepository.iterator().hasNext()
|
||||
? clientRegistrationRepository.iterator().next()
|
||||
: null;
|
||||
|
||||
Assert.notNull(clientRegistration, "There must be a valid client registration");
|
||||
http.oauth2ResourceServer(configurer -> configurer.jwt(configurer2 -> {
|
||||
if (clientRegistration.getProviderDetails().getJwkSetUri() == null) {
|
||||
configurer2.decoder(JwtDecoders.fromIssuerLocation(clientRegistration.getProviderDetails().getIssuerUri()));
|
||||
} else {
|
||||
configurer2.jwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri());
|
||||
}
|
||||
}));
|
||||
|
||||
oidcBearerTokenAuthenticationFilter.setClientRegistration(clientRegistration);
|
||||
|
||||
http.addFilterAfter(oidcBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class);
|
||||
} else {
|
||||
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm());
|
||||
|
||||
http.addFilterBefore(new Filter() {
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
userAuthenticationFilter.init(filterConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(final ServletRequest request, final ServletResponse response,
|
||||
final FilterChain chain) throws IOException, ServletException {
|
||||
userAuthenticationFilter.doFilter(request, response, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
userAuthenticationFilter.destroy();
|
||||
}
|
||||
}, RequestHeaderAuthenticationFilter.class);
|
||||
http
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(basicAuthEntryPoint));
|
||||
if (oauth2ResourceServerCustomizer != null) {
|
||||
http.oauth2ResourceServer(oauth2ResourceServerCustomizer);
|
||||
}
|
||||
if (oauth2ResourceServerCustomizer == null || securityProperties.isAllowHttpBasicOnOAuthEnabled()) {
|
||||
http.httpBasic(configurer -> {
|
||||
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm());
|
||||
configurer.authenticationEntryPoint(basicAuthEntryPoint);
|
||||
});
|
||||
}
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.cors", name = "enabled")
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
final CorsConfiguration configuration = corsConfiguration();
|
||||
return request -> configuration;
|
||||
}
|
||||
|
||||
private CorsConfiguration corsConfiguration() {
|
||||
private CorsConfigurationSource corsConfigurationSource() {
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
corsConfiguration.setAllowedOrigins(securityProperties.getCors().getAllowedOrigins());
|
||||
@@ -584,8 +481,7 @@ public class SecurityManagedConfiguration {
|
||||
corsConfiguration.setAllowedHeaders(securityProperties.getCors().getAllowedHeaders());
|
||||
corsConfiguration.setAllowedMethods(securityProperties.getCors().getAllowedMethods());
|
||||
corsConfiguration.setExposedHeaders(securityProperties.getCors().getExposedHeaders());
|
||||
|
||||
return corsConfiguration;
|
||||
return request -> corsConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,5 @@ hawkbit.server.ddi.security.authentication.header.enabled=false
|
||||
hawkbit.server.ddi.security.authentication.header.authority=
|
||||
hawkbit.server.ddi.security.authentication.targettoken.enabled=false
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
|
||||
hawkbit.server.download.anonymous.enabled=false
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.key=
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.key=
|
||||
hawkbit.server.download.anonymous.enabled=false
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.im.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
|
||||
/**
|
||||
* Filter to integrate into the SP security filter-chain. The filter is called
|
||||
* in any remote call through HTTP except the SP login screen. E.g. using the SP
|
||||
* REST-API. To authenticate user e.g. using Basic-Authentication implement the
|
||||
* {@link #doFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse, jakarta.servlet.FilterChain)}
|
||||
* method.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface UserAuthenticationFilter {
|
||||
|
||||
/**
|
||||
* @see Filter#init(FilterConfig)
|
||||
*
|
||||
* @param filterConfig
|
||||
* the filter config
|
||||
*/
|
||||
void init(FilterConfig filterConfig) throws ServletException;
|
||||
|
||||
/**
|
||||
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
|
||||
*
|
||||
* @param request
|
||||
* the servlet request
|
||||
* @param response
|
||||
* the servlet response
|
||||
* @param chain
|
||||
* the filterchain
|
||||
* @throws IOException
|
||||
* cannot read from request
|
||||
* @throws ServletException
|
||||
* servlet exception
|
||||
*/
|
||||
// this declaration of multiple checked exception is necessary so it's
|
||||
// aligned with the servlet API.
|
||||
@SuppressWarnings("squid:S1160")
|
||||
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException;
|
||||
|
||||
/**
|
||||
* @see Filter#destroy()
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
}
|
||||
@@ -49,6 +49,10 @@ public class HawkbitSecurityProperties {
|
||||
* Basic authentication realm, see https://tools.ietf.org/html/rfc2617#page-3 .
|
||||
*/
|
||||
private String basicRealm = "hawkBit";
|
||||
/**
|
||||
* If to allow http authentication when there is OAuth2 authentication enabled.
|
||||
*/
|
||||
private boolean allowHttpBasicOnOAuthEnabled = false;
|
||||
|
||||
/**
|
||||
* Security configuration related to CORS.
|
||||
|
||||
Reference in New Issue
Block a user