Split SecurityManagedConfiguration to mgmt and ddi starters (#2014)

* SecurityManagedConfiguration is moved to hawkbit-rest-core with commons for mgmt and ddi only
* Configurations for DDI and Management API are moved to respective starters
* hawkbit-http-security is removed - DosFilter (as common) is moved in hawkbit-rest-security, rest to the ddi starter as used only there
* some classes are moved into different packages - it is a bad practice to have same packet into multiple artifacts

_release_notes_

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-11-12 12:06:56 +02:00
committed by GitHub
parent 1e6c9d5efe
commit c85518be3c
34 changed files with 689 additions and 676 deletions

View File

@@ -0,0 +1,164 @@
/**
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* 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.autoconfigure.ddi;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.autoconfigure.ddi.security.ControllerTenantAwareAuthenticationDetailsSource;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticateAnonymousDownloadFilter;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.rest.SecurityManagedConfiguration;
import org.eclipse.hawkbit.rest.security.DosFilter;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.MdcHandler;
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.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
/**
* Security configuration for the hawkBit server DDI download interface.
*/
@Slf4j
@Configuration
class ControllerDownloadSecurityConfiguration {
private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING +
"/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*";
private final ControllerManagement controllerManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final TenantAware tenantAware;
private final DdiSecurityProperties ddiSecurityConfiguration;
private final HawkbitSecurityProperties securityProperties;
private final SystemSecurityContext systemSecurityContext;
@Autowired
ControllerDownloadSecurityConfiguration(final ControllerManagement controllerManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final DdiSecurityProperties ddiSecurityConfiguration,
final HawkbitSecurityProperties securityProperties, final SystemSecurityContext systemSecurityContext) {
this.controllerManagement = controllerManagement;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.tenantAware = tenantAware;
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
this.securityProperties = securityProperties;
this.systemSecurityContext = systemSecurityContext;
}
/**
* 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
*/
@Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean<DosFilter> dosFilterDDIDL(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean = SecurityManagedConfiguration.dosFilter(List.of(DDI_DL_ANT_MATCHER),
securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setOrder(SecurityManagedConfiguration.DOS_FILTER_ORDER);
filterRegBean.setName("dosDDiDlFilter");
return filterRegBean;
}
@Bean
@Order(301)
protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = ControllerSecurityConfiguration.setAuthenticationManager(
http, ddiSecurityConfiguration);
http
.securityMatcher(DDI_DL_ANT_MATCHER)
.csrf(AbstractHttpConfigurer::disable);
if (securityProperties.isRequireSsl()) {
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
}
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
log.warn(
SecurityManagedConfiguration.ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
List.of(new SimpleGrantedAuthority(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
http
.securityContext(AbstractHttpConfigurer::disable)
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
} else {
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
ddiSecurityConfiguration.getRp().getCnHeader(),
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
tenantAware, systemSecurityContext);
securityHeaderFilter.setAuthenticationManager(authenticationManager);
securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
securityTokenFilter.setAuthenticationManager(authenticationManager);
securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext);
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager);
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
http
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
.anonymous(AbstractHttpConfigurer::disable)
.addFilter(securityHeaderFilter)
.addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter)
.addFilter(controllerAnonymousDownloadFilter)
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
(request, response, authException) -> response.setStatus(HttpStatus.UNAUTHORIZED.value())))
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
}
MdcHandler.Filter.addMdcFilter(http);
return http.build();
}
}

View File

@@ -0,0 +1,178 @@
/**
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* 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.autoconfigure.ddi;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.autoconfigure.ddi.security.ControllerTenantAwareAuthenticationDetailsSource;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.rest.SecurityManagedConfiguration;
import org.eclipse.hawkbit.rest.security.DosFilter;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.MdcHandler;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
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.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.http.SessionCreationPolicy;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
/**
* Security configuration for the hawkBit server DDI interface.
*/
@Slf4j
@Configuration
@EnableWebSecurity
class ControllerSecurityConfiguration {
private static final String[] DDI_ANT_MATCHERS = {
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/confirmationBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/deploymentBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/installedBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
private final ControllerManagement controllerManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final TenantAware tenantAware;
private final DdiSecurityProperties ddiSecurityConfiguration;
private final HawkbitSecurityProperties securityProperties;
private final SystemSecurityContext systemSecurityContext;
@Autowired
ControllerSecurityConfiguration(final ControllerManagement controllerManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final DdiSecurityProperties ddiSecurityConfiguration,
final HawkbitSecurityProperties securityProperties, final SystemSecurityContext systemSecurityContext) {
this.controllerManagement = controllerManagement;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.tenantAware = tenantAware;
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
this.securityProperties = securityProperties;
this.systemSecurityContext = systemSecurityContext;
}
/**
* 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
*/
@Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
protected FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean<DosFilter> filterRegBean =
SecurityManagedConfiguration.dosFilter(List.of(DDI_ANT_MATCHERS),
securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setOrder(SecurityManagedConfiguration.DOS_FILTER_ORDER);
filterRegBean.setName("dosDDiFilter");
return filterRegBean;
}
@Bean
@Order(300)
protected SecurityFilterChain filterChainDDI(final HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
http
.securityMatcher(DDI_ANT_MATCHERS)
.csrf(AbstractHttpConfigurer::disable);
if (securityProperties.isRequireSsl()) {
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
}
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
log.warn(SecurityManagedConfiguration.ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
List.of(new SimpleGrantedAuthority(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
http
.securityContext(AbstractHttpConfigurer::disable)
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
} else {
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
ddiSecurityConfiguration.getRp().getCnHeader(),
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
tenantAware, systemSecurityContext);
securityHeaderFilter.setAuthenticationManager(authenticationManager);
securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
securityTokenFilter.setAuthenticationManager(authenticationManager);
securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
http
.authorizeHttpRequests(amrmRegistry ->
amrmRegistry.anyRequest().authenticated())
.anonymous(AbstractHttpConfigurer::disable)
.addFilter(securityHeaderFilter)
.addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter)
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
(request, response, authException) ->
response.setStatus(HttpStatus.UNAUTHORIZED.value())))
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
}
MdcHandler.Filter.addMdcFilter(http);
return http.build();
}
static AuthenticationManager setAuthenticationManager(final HttpSecurity http, final DdiSecurityProperties ddiSecurityConfiguration)
throws Exception {
// configure authentication manager
final AuthenticationManager authenticationManager =
http
.getSharedObject(AuthenticationManagerBuilder.class)
.authenticationProvider(
new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()))
.build();
http.authenticationManager(authenticationManager);
return authenticationManager;
}
}

View File

@@ -20,6 +20,6 @@ import org.springframework.context.annotation.Import;
@Configuration
@ConditionalOnClass(DdiApiConfiguration.class)
@Import(DdiApiConfiguration.class)
public class DDiApiAutoConfiguration {
public class DdiApiAutoConfiguration {
}

View File

@@ -0,0 +1,186 @@
/**
* 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.autoconfigure.ddi.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import jakarta.servlet.FilterChain;
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.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.UrlUtils;
import org.slf4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.AntPathMatcher;
/**
* An abstraction for all controller based security to parse the e.g. the tenant
* name from the URL and the controller ID from the URL to do security checks
* based on this information.
*/
public abstract class AbstractHttpControllerAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
protected TenantConfigurationManagement tenantConfigurationManagement;
protected TenantAware tenantAware;
protected SystemSecurityContext systemSecurityContext;
private static final String TENANT_PLACE_HOLDER = "tenant";
private static final String CONTROLLER_ID_PLACE_HOLDER = "controllerId";
/**
* requestURIPathPattern the request URI path pattern in ANT style
* containing the placeholder key for retrieving the principal from the URI
* request. e.g."/{tenant}/controller/v1/{controllerId}
*/
private static final String CONTROLLER_REQUEST_ANT_PATTERN = "/{" + TENANT_PLACE_HOLDER + "}/controller/v1" + "/{"
+ CONTROLLER_ID_PLACE_HOLDER + "}/**";
private static final String CONTROLLER_DL_REQUEST_ANT_PATTERN = "/{" + TENANT_PLACE_HOLDER
+ "}/controller/artifacts/v1/**";
private final AntPathMatcher pathExtractor;
private PreAuthenticationFilter abstractControllerAuthenticationFilter;
/**
* Constructor for subclasses.
*
* @param tenantConfigurationManagement the tenant configuration service
* @param tenantAware the tenant aware service
* @param systemSecurityContext the system security context
*/
protected AbstractHttpControllerAuthenticationFilter(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final SystemSecurityContext systemSecurityContext) {
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.tenantAware = tenantAware;
this.systemSecurityContext = systemSecurityContext;
pathExtractor = new AntPathMatcher();
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() != null) {
log().trace("Request is already authenticated. Skip filter");
chain.doFilter(request, response);
return;
}
if (!(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
final DmfTenantSecurityToken securityToken = createTenantSecurityTokenVariables((HttpServletRequest) request);
if (securityToken == null) {
chain.doFilter(request, response);
return;
}
abstractControllerAuthenticationFilter = createControllerAuthenticationFilter();
if (abstractControllerAuthenticationFilter.isEnable(securityToken)) {
super.doFilter(request, response, chain);
} else {
log().debug("Filter is disabled for the tenant {}", securityToken.getTenant());
chain.doFilter(request, response);
}
}
@Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authResult) throws IOException, ServletException {
final Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(authResult.getAuthorities());
authorities.addAll(abstractControllerAuthenticationFilter.getSuccessfulAuthenticationAuthorities());
final PreAuthenticatedAuthenticationToken authTokenWithGrantedAuthorities = new PreAuthenticatedAuthenticationToken(
authResult.getPrincipal(), authResult.getCredentials(), authorities);
authTokenWithGrantedAuthorities.setDetails(authResult.getDetails());
super.successfulAuthentication(request, response, authTokenWithGrantedAuthorities);
}
@Override
protected Object getPreAuthenticatedPrincipal(final HttpServletRequest request) {
final DmfTenantSecurityToken securityToken = createTenantSecurityTokenVariables(request);
if (securityToken == null) {
return null;
}
return abstractControllerAuthenticationFilter.getPreAuthenticatedPrincipal(securityToken);
}
@Override
protected Object getPreAuthenticatedCredentials(final HttpServletRequest request) {
final DmfTenantSecurityToken securityToken = createTenantSecurityTokenVariables(request);
if (securityToken == null) {
return null;
}
return abstractControllerAuthenticationFilter.getPreAuthenticatedCredentials(securityToken);
}
protected abstract PreAuthenticationFilter createControllerAuthenticationFilter();
protected abstract Logger log();
/**
* Extracts tenant and controllerId from the request URI as path variables.
*
* @param request the Http request to extract the path variables.
* @return the extracted {@link DmfTenantSecurityToken} or {@code null} if the
* request does not match the pattern and no variables could be
* extracted
*/
protected DmfTenantSecurityToken createTenantSecurityTokenVariables(final HttpServletRequest request) {
final String requestURI = request.getRequestURI();
if (pathExtractor.match(request.getContextPath() + CONTROLLER_REQUEST_ANT_PATTERN, requestURI)) {
log().debug("retrieving principal from URI request {}", requestURI);
final Map<String, String> extractUriTemplateVariables = pathExtractor
.extractUriTemplateVariables(request.getContextPath() + CONTROLLER_REQUEST_ANT_PATTERN, requestURI);
final String controllerId = UrlUtils.decodeUriValue(extractUriTemplateVariables.get(CONTROLLER_ID_PLACE_HOLDER));
final String tenant = UrlUtils.decodeUriValue(extractUriTemplateVariables.get(TENANT_PLACE_HOLDER));
log().trace("Parsed tenant {} and controllerId {} from path request {}", tenant, controllerId, requestURI);
return createTenantSecurityTokenVariables(request, tenant, controllerId);
} else if (pathExtractor.match(request.getContextPath() + CONTROLLER_DL_REQUEST_ANT_PATTERN, requestURI)) {
log().debug("retrieving path variables from URI request {}", requestURI);
final Map<String, String> extractUriTemplateVariables = pathExtractor.extractUriTemplateVariables(
request.getContextPath() + CONTROLLER_DL_REQUEST_ANT_PATTERN, requestURI);
final String tenant = UrlUtils.decodeUriValue(extractUriTemplateVariables.get(TENANT_PLACE_HOLDER));
log().trace("Parsed tenant {} from path request {}", tenant, requestURI);
return createTenantSecurityTokenVariables(request, tenant, "anonymous");
} else {
log().trace("request {} does not match the path pattern {}, request gets ignored", requestURI,
CONTROLLER_REQUEST_ANT_PATTERN);
return null;
}
}
private DmfTenantSecurityToken createTenantSecurityTokenVariables(final HttpServletRequest request,
final String tenant, final String controllerId) {
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(tenant, null, controllerId, null);
Collections.list(request.getHeaderNames())
.forEach(header -> securityToken.putHeader(header, request.getHeader(header)));
return securityToken;
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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.autoconfigure.ddi.security;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.security.TenantAwareWebAuthenticationDetails;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.util.UrlUtils;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.util.AntPathMatcher;
/**
* An {@link AuthenticationDetailsSource} implementation which retrieves the
* tenant from a request pattern {@link #TENANT_AWARE_CONTROLLER_PATTERN} and
* stores the retrieved tenant in the {@link TenantAwareAuthenticationDetails}.
*/
@Slf4j
public class ControllerTenantAwareAuthenticationDetailsSource
implements AuthenticationDetailsSource<HttpServletRequest, TenantAwareAuthenticationDetails> {
private static final String TENANT_AWARE_CONTROLLER_PATTERN = "/{tenant}/controller/**";
private static final String TENANT_PLACE_HOLDER = "tenant";
private final AntPathMatcher pathExtractor;
/**
* Constructor.
*/
public ControllerTenantAwareAuthenticationDetailsSource() {
pathExtractor = new AntPathMatcher();
}
@Override
public TenantAwareAuthenticationDetails buildDetails(final HttpServletRequest request) {
return new TenantAwareWebAuthenticationDetails(getTenantFromRequestUri(request), request.getRemoteAddr(), true);
}
private String getTenantFromRequestUri(final HttpServletRequest request) {
final String requestURI = request.getRequestURI();
log.debug("retrieving tenant from URI request {}", requestURI);
final String requestPathPattern = request.getContextPath() + TENANT_AWARE_CONTROLLER_PATTERN;
if (!pathExtractor.match(requestPathPattern, requestURI)) {
log.info("Controller request not matching tenant aware request pattern requestpath: {}, pattern {}",
requestURI, TENANT_AWARE_CONTROLLER_PATTERN);
return null;
}
final Map<String, String> extractUriTemplateVariables = pathExtractor
.extractUriTemplateVariables(requestPathPattern, requestURI);
if (log.isTraceEnabled()) {
log.trace("Parsed path variables {} using tenant {}", extractUriTemplateVariables,
extractUriTemplateVariables.get(TENANT_PLACE_HOLDER));
}
return UrlUtils.decodeUriValue(extractUriTemplateVariables.get(TENANT_PLACE_HOLDER));
}
}

View File

@@ -0,0 +1,52 @@
/**
* 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.autoconfigure.ddi.security;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
/**
* An pre-authenticated processing filter which add the
* {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE_ANONYMOUS} to the
* security context in case the anonymous download is allowed through
* configuration.
*/
@Slf4j
public class HttpControllerPreAuthenticateAnonymousDownloadFilter extends AbstractHttpControllerAuthenticationFilter {
/**
* Constructor.
*
* @param tenantConfigurationManagement the system management service to retrieve configuration properties
* @param tenantAware the tenant aware service to get configuration for the specific tenant
* @param systemSecurityContext the system security context
*/
public HttpControllerPreAuthenticateAnonymousDownloadFilter(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final SystemSecurityContext systemSecurityContext) {
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
protected Logger log() {
return log;
}
}

View File

@@ -0,0 +1,67 @@
/**
* 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.autoconfigure.ddi.security;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
/**
* An pre-authenticated processing filter which extracts (if enabled through
* configuration) the possibility to authenticate a target based on its target
* security-token with the {@code Authorization} HTTP header.
* {@code Example Header: Authorization: TargetToken
* 5d8fSD54fdsFG98DDsa.}
*
* The {@code Authorization} header is a HTTP standard and reverse proxy or
* other proxies will keep the Authorization headers untouched instead of maybe
* custom headers which have then weird side-effects. Furthermore frameworks are
* aware of the sensitivity of the Authorization header and do not log it and
* store it somewhere.
*/
@Slf4j
public class HttpControllerPreAuthenticateSecurityTokenFilter extends AbstractHttpControllerAuthenticationFilter {
private final ControllerManagement controllerManagement;
/**
* Constructor.
*
* @param tenantConfigurationManagement the system management service to retrieve configuration
* properties
* @param tenantAware the tenant aware service to get configuration for the specific
* tenant
* @param controllerManagement the controller management to retrieve the specific target
* security token to verify
* @param systemSecurityContext the system security context
*/
public HttpControllerPreAuthenticateSecurityTokenFilter(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final ControllerManagement controllerManagement, final SystemSecurityContext systemSecurityContext) {
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
this.controllerManagement = controllerManagement;
}
@Override
protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticateSecurityTokenFilter(tenantConfigurationManagement, controllerManagement,
tenantAware, systemSecurityContext);
}
@Override
protected Logger log() {
return log;
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.autoconfigure.ddi.security;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
/**
* Extract the {@code Authorization} header is a HTTP standard and reverse proxy
* or other proxies will keep the Authorization headers untouched instead of
* maybe custom headers which have then weird side-effects. Furthermore
* frameworks are aware of the sensitivity of the Authorization header and do
* not log it and store it somewhere.
*/
@Slf4j
public class HttpControllerPreAuthenticatedGatewaySecurityTokenFilter
extends AbstractHttpControllerAuthenticationFilter {
/**
* Constructor.
*
* @param tenantConfigurationManagement the system management service to retrieve configuration
* properties
* @param tenantAware the tenant aware service to get configuration for the specific
* tenant
* @param systemSecurityContext the system security context
*/
public HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final SystemSecurityContext systemSecurityContext) {
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedGatewaySecurityTokenFilter(tenantConfigurationManagement, tenantAware,
systemSecurityContext);
}
@Override
protected Logger log() {
return log;
}
}

View File

@@ -0,0 +1,66 @@
/**
* 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.autoconfigure.ddi.security;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
/**
* An pre-authenticated processing filter which extracts the principal from a
* request URI and the credential from a request header.
*/
@Slf4j
public class HttpControllerPreAuthenticatedSecurityHeaderFilter extends AbstractHttpControllerAuthenticationFilter {
private final String caCommonNameHeader;
private final String caAuthorityNameHeader;
/**
* Creates a new {@link org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter}, in
* case the HTTP request matches the given pattern the principal is parsed
* from the HTTP request with the given URI pattern, in case the URI pattern
* does not match the current request then only the existence of the
* configured header field is checked.
*
* @param caCommonNameHeader the http-header which holds the common-name of the certificate
* @param caAuthorityNameHeader the http-header which holds the ca-authority name of the
* certificate
* @param tenantConfigurationManagement the tenant configuration management service to retrieve
* configuration properties to check if the header authentication
* is enabled for this tenant
* @param tenantAware the tenant aware service to get configuration for the specific
* tenant
* @param systemSecurityContext the system security context
*/
public HttpControllerPreAuthenticatedSecurityHeaderFilter(final String caCommonNameHeader,
final String caAuthorityNameHeader, final TenantConfigurationManagement tenantConfigurationManagement,
final TenantAware tenantAware, final SystemSecurityContext systemSecurityContext) {
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
this.caCommonNameHeader = caCommonNameHeader;
this.caAuthorityNameHeader = caAuthorityNameHeader;
}
@Override
protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedSecurityHeaderFilter(
caCommonNameHeader, caAuthorityNameHeader,
tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
protected Logger log() {
return log;
}
}

View File

@@ -1 +1,4 @@
org.eclipse.hawkbit.autoconfigure.ddi.DDiApiAutoConfiguration
org.eclipse.hawkbit.autoconfigure.ddi.DdiApiAutoConfiguration
org.eclipse.hawkbit.rest.SecurityManagedConfiguration
org.eclipse.hawkbit.autoconfigure.ddi.ControllerSecurityConfiguration
org.eclipse.hawkbit.autoconfigure.ddi.ControllerDownloadSecurityConfiguration