MDC hanlder refactoring (#1911)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-10-23 09:20:15 +03:00
committed by GitHub
parent 64771e9f4e
commit 8da475dff0
5 changed files with 80 additions and 42 deletions

View File

@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.im.authentication.TenantAwareUserProperties.User;
import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.InMemoryUserAuthoritiesResolver; import org.eclipse.hawkbit.security.InMemoryUserAuthoritiesResolver;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.MDCHandler; import org.eclipse.hawkbit.security.MdcHandler;
import org.eclipse.hawkbit.security.SecurityContextSerializer; import org.eclipse.hawkbit.security.SecurityContextSerializer;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
@@ -124,8 +124,8 @@ public class SecurityAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MDCHandler mdcHandler() { public MdcHandler mdcHandler() {
return MDCHandler.getInstance(); return MdcHandler.getInstance();
} }
@Bean @Bean

View File

@@ -33,7 +33,7 @@ import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateAnonymousDownlo
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.MDCHandler; import org.eclipse.hawkbit.security.MdcHandler;
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;
@@ -62,7 +62,6 @@ 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.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.firewall.FirewalledRequest; import org.springframework.security.web.firewall.FirewalledRequest;
@@ -203,7 +202,7 @@ public class SecurityManagedConfiguration {
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
} }
MDCHandler.Filter.addLoggingFilter(http); MdcHandler.Filter.addMdcFilter(http);
return http.build(); return http.build();
} }
@@ -320,7 +319,7 @@ public class SecurityManagedConfiguration {
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
} }
MDCHandler.Filter.addLoggingFilter(http); MdcHandler.Filter.addMdcFilter(http);
return http.build(); return http.build();
} }
@@ -453,7 +452,7 @@ public class SecurityManagedConfiguration {
httpSecurityCustomizer.customize(http); httpSecurityCustomizer.customize(http);
} }
MDCHandler.Filter.addLoggingFilter(http); MdcHandler.Filter.addMdcFilter(http);
return http.build(); return http.build();
} }

View File

@@ -30,36 +30,34 @@ import java.util.Objects;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MDCHandler { public class MdcHandler {
public static String MDC_KEY_TENANT = "tenant"; public static String MDC_KEY_TENANT = "tenant";
public static String MDC_KEY_USER = "user"; public static String MDC_KEY_USER = "user";
private static final MDCHandler SINGLETON = new MDCHandler(); private static final MdcHandler SINGLETON = new MdcHandler();
@Value("${hawkbit.logging.mdchandler.enabled:true}") @Value("${hawkbit.logging.mdchandler.enabled:true}")
private boolean mdcEnabled; private boolean mdcEnabled;
@Autowired(required = false) @Autowired(required = false)
private SpringSecurityAuditorAware springSecurityAuditorAware = new SpringSecurityAuditorAware(); private SpringSecurityAuditorAware springSecurityAuditorAware = new SpringSecurityAuditorAware();
@Autowired(required = false)
private SystemSecurityContext securityContext;
/** /**
* @return The holder singleton instance. * @return The holder singleton instance.
*/ */
public static MDCHandler getInstance() { public static MdcHandler getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Executes callable and returns the result. If MDC is enabled, it sets the tenant and / or user in the MDC context. * Executes callable and returns the result. If MDC is enabled, it sets the tenant and / or user from the authentication in the MDC context.
* *
* @param <T> the return type * @param <T> the return type
* @param callable the callable to execute * @param callable the callable to execute
* @return the result * @return the result
* @throws Exception if thrown by the callable * @throws Exception if thrown by the callable
*/ */
public <T> T withLogging(final Callable<T> callable) throws Exception { public <T> T callWithAuth(final Callable<T> callable) throws Exception {
if (!mdcEnabled) { if (!mdcEnabled) {
return callable.call(); return callable.call();
} }
@@ -76,30 +74,25 @@ public class MDCHandler {
tenant = null; tenant = null;
} }
final String currentTenant = MDC.get(MDC_KEY_TENANT); final String user = springSecurityAuditorAware
if (Objects.equals(currentTenant, tenant)) { .getCurrentAuditor()
return putUserAndCall(callable); .filter(username -> !username.equals("system")) // null and system are the same - system user
} else { .orElse(null);
put(MDC_KEY_TENANT, tenant);
try { return callWithTenantAndUser0(callable, tenant, user);
return putUserAndCall(callable);
} finally {
put(MDC_KEY_TENANT, currentTenant);
}
}
} }
/** /**
* With logging throwing Runtime Exception (withLoggingRE). Calls the {@link #withLogging(Callable)} method and * Executes callable and returns the result. If MDC is enabled, it sets the tenant and / or user from the authentication in the MDC context.
* wraps any catchable exception into a {@link RuntimeException}. * Calls the {@link #callWithAuth(Callable)} method and wraps any catchable exception into a {@link RuntimeException}.
* *
* @param <T> the return type * @param <T> the return type
* @param callable the callable to execute * @param callable the callable to execute
* @return the result * @return the result
*/ */
public <T> T withLoggingRE(final Callable<T> callable) { public <T> T callWithAuthRE(final Callable<T> callable) {
try { try {
return withLogging(callable); return callWithAuth(callable);
} catch (final RuntimeException re) { } catch (final RuntimeException re) {
throw re; throw re;
} catch (final Exception e) { } catch (final Exception e) {
@@ -107,12 +100,58 @@ public class MDCHandler {
} }
} }
private <T> T putUserAndCall(final Callable<T> callable) throws Exception { /**
final String user = springSecurityAuditorAware * Executes callable and returns the result. If MDC is enabled, it sets the tenant and / or user in the MDC context.
.getCurrentAuditor() *
.filter(username -> !username.equals("system")) // null and system are the same - system user * @param <T> the return type
.orElse(null); * @param callable the callable to execute
* @param tenant the tenant to set in the MDC context
* @param user the user to set in the MDC context
* @return the result
*/
public <T> T callWithTenantAndUser(final Callable<T> callable, final String tenant, final String user) throws Exception {
if (!mdcEnabled) {
return callable.call();
}
return callWithTenantAndUser0(callable, tenant, user);
}
/**
* Executes callable and returns the result. If MDC is enabled, it sets the tenant and / or user from the authentication in the MDC context.
* Calls the {@link #callWithTenantAndUser(Callable, String, String)} method and wraps any catchable exception into a {@link RuntimeException}.
*
* @param <T> the return type
* @param callable the callable to execute
* @param tenant the tenant to set in the MDC context
* @param user the user to set in the MDC context
* @return the result
*/
public <T> T callWithTenantAndUserRE(final Callable<T> callable, final String tenant, final String user) {
try {
return callWithTenantAndUser(callable, tenant, user);
} catch (final RuntimeException re) {
throw re;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private static <T> T callWithTenantAndUser0(final Callable<T> callable, final String tenant, final String user) throws Exception {
final String currentTenant = MDC.get(MDC_KEY_TENANT);
if (Objects.equals(currentTenant, tenant)) {
return callWithUser(callable, user);
} else {
put(MDC_KEY_TENANT, tenant);
try {
return callWithUser(callable, user);
} finally {
put(MDC_KEY_TENANT, currentTenant);
}
}
}
private static <T> T callWithUser(final Callable<T> callable, final String user) throws Exception {
final String currentUser = MDC.get(MDC_KEY_USER); final String currentUser = MDC.get(MDC_KEY_USER);
if (Objects.equals(currentUser, user)) { if (Objects.equals(currentUser, user)) {
return callable.call(); return callable.call();
@@ -137,17 +176,17 @@ public class MDCHandler {
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Filter { public static class Filter {
public static void addLoggingFilter(final HttpSecurity httpSecurity) { public static void addMdcFilter(final HttpSecurity httpSecurity) {
httpSecurity.addFilterBefore(new OncePerRequestFilter() { httpSecurity.addFilterBefore(new OncePerRequestFilter() {
private final MDCHandler mdcFilter = MDCHandler.getInstance(); private final MdcHandler mdcFilter = MdcHandler.getInstance();
@Override @Override
protected void doFilterInternal( protected void doFilterInternal(
final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {
try { try {
mdcFilter.withLogging(() -> { mdcFilter.callWithAuth(() -> {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
return null; return null;
}); });

View File

@@ -142,7 +142,7 @@ public class SecurityContextTenantAware implements ContextAware {
} else { } else {
SecurityContextHolder.setContext(securityContext); SecurityContextHolder.setContext(securityContext);
try { try {
return MDCHandler.getInstance().withLoggingRE(supplier::get); return MdcHandler.getInstance().callWithAuthRE(supplier::get);
} finally { } finally {
SecurityContextHolder.setContext(originalContext); SecurityContextHolder.setContext(originalContext);
} }
@@ -153,7 +153,7 @@ public class SecurityContextTenantAware implements ContextAware {
final SecurityContext currentContext = SecurityContextHolder.getContext(); final SecurityContext currentContext = SecurityContextHolder.getContext();
SystemSecurityContext.setSystemContext(currentContext); SystemSecurityContext.setSystemContext(currentContext);
try { try {
return MDCHandler.getInstance().withLoggingRE(tenantRunner::run); return MdcHandler.getInstance().callWithAuthRE(tenantRunner::run);
} finally { } finally {
SecurityContextHolder.setContext(currentContext); SecurityContextHolder.setContext(currentContext);
} }

View File

@@ -110,7 +110,7 @@ public class SystemSecurityContext {
log.debug("Entering system code execution"); log.debug("Entering system code execution");
return tenantAware.runAsTenant(tenant, () -> { return tenantAware.runAsTenant(tenant, () -> {
setSystemContext(SecurityContextHolder.getContext()); setSystemContext(SecurityContextHolder.getContext());
return MDCHandler.getInstance().withLoggingRE(callable); return MdcHandler.getInstance().callWithAuthRE(callable);
}); });
} finally { } finally {
SecurityContextHolder.setContext(oldContext); SecurityContextHolder.setContext(oldContext);
@@ -139,7 +139,7 @@ public class SystemSecurityContext {
try { try {
return tenantAware.runAsTenant(tenant, () -> { return tenantAware.runAsTenant(tenant, () -> {
setCustomSecurityContext(tenant, oldContext.getAuthentication().getPrincipal(), authorities); setCustomSecurityContext(tenant, oldContext.getAuthentication().getPrincipal(), authorities);
return MDCHandler.getInstance().withLoggingRE(callable); return MdcHandler.getInstance().callWithAuthRE(callable);
}); });
} finally { } finally {
SecurityContextHolder.setContext(oldContext); SecurityContextHolder.setContext(oldContext);