Add MDC context in SecurityContdxtTenantAware (#1818)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-08-13 09:06:53 +03:00
committed by GitHub
parent 96d8831f15
commit 9bb61fd829
3 changed files with 73 additions and 92 deletions

View File

@@ -51,6 +51,14 @@ public class MDCHandler {
return SINGLETON;
}
/**
* Executes callable and returns the result. If MDC is enabled, it sets the tenant and / or user in the MDC context.
*
* @param <T> the return type
* @param callable the callable to execute
* @return the result
* @throws Exception if thrown by the callable
*/
public <T> T withLogging(final Callable<T> callable) throws Exception {
if (!mdcEnabled) {
return callable.call();
@@ -81,7 +89,25 @@ public class MDCHandler {
}
}
private <T> T putUserAndCall(final Callable<T> callable) throws WrappedException {
/**
* With logging throwing Runtime Exception (wihtLoggingRE). Calls the {@link #withLogging(Callable)} method and
* wraps any catchable exception into a {@link RuntimeException}.
*
* @param <T> the return type
* @param callable the callable to execute
* @return the result
*/
public <T> T withLoggingRE(final Callable<T> callable) {
try {
return withLogging(callable);
} catch (final RuntimeException re) {
throw re;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private <T> T putUserAndCall(final Callable<T> callable) throws Exception {
final String user = springSecurityAuditorAware
.getCurrentAuditor()
.filter(username -> !username.equals("system")) // null and system are the same - system user
@@ -89,21 +115,15 @@ public class MDCHandler {
.orElse(null);
final String currentUser = MDC.get(MDC_KEY_USER);
try {
if (Objects.equals(currentUser, user)) {
if (Objects.equals(currentUser, user)) {
return callable.call();
} else {
put(MDC_KEY_USER, user);
try {
return callable.call();
} else {
put(MDC_KEY_USER, user);
try {
return callable.call();
} finally {
put(MDC_KEY_USER, currentUser);
}
} finally {
put(MDC_KEY_USER, currentUser);
}
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new WrappedException(e);
}
}
@@ -115,18 +135,6 @@ public class MDCHandler {
}
}
// Wraps catchable exceptions to rethrow
public static class WrappedException extends Exception {
public WrappedException(final Throwable cause) {
super(cause);
}
public RuntimeException toRuntimeException() {
return new RuntimeException(getCause() == null ? this : getCause());
}
}
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Filter {
@@ -144,19 +152,9 @@ public class MDCHandler {
filterChain.doFilter(request, response);
return null;
});
} catch (final RuntimeException re) {
throw re;
} catch (final WrappedException we) {
final Throwable cause = we.getCause();
if (cause instanceof ServletException se) {
throw se;
} else if (cause instanceof IOException ioe) {
throw ioe;
} else {
throw we.toRuntimeException();
}
} catch (final ServletException | IOException | RuntimeException e) {
throw e;
} catch (final Exception e) {
// should never be here - if mdc is handler is enabled non-runtime exceptions are always wrapped
throw new RuntimeException(e);
}
}

View File

@@ -16,6 +16,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.ContextAware;
@@ -43,8 +44,8 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
public class SecurityContextTenantAware implements ContextAware {
public static final String SYSTEM_USER = "system";
private static final Collection<? extends GrantedAuthority> SYSTEM_AUTHORITIES = Collections
.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.SYSTEM_ROLE));
private static final Collection<? extends GrantedAuthority> SYSTEM_AUTHORITIES =
Collections.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.SYSTEM_ROLE));
private final UserAuthoritiesResolver authoritiesResolver;
private final SecurityContextSerializer securityContextSerializer;
@@ -66,11 +67,8 @@ public class SecurityContextTenantAware implements ContextAware {
* Creates the {@link SecurityContextTenantAware} based on the given
* {@link UserAuthoritiesResolver}.
*
* @param authoritiesResolver
* Resolver to retrieve the authorities for a given user. Must
* not be <code>null</code>.
* @param securityContextSerializer
* Serializer that is used to serialize / deserialize {@link SecurityContext}s.
* @param authoritiesResolver Resolver to retrieve the authorities for a given user. Must not be <code>null</code>.
* @param securityContextSerializer Serializer that is used to serialize / deserialize {@link SecurityContext}s.
*/
public SecurityContextTenantAware(final UserAuthoritiesResolver authoritiesResolver, @Nullable final SecurityContextSerializer securityContextSerializer) {
this.authoritiesResolver = authoritiesResolver;
@@ -106,24 +104,25 @@ public class SecurityContextTenantAware implements ContextAware {
return null;
}
@Override
public Optional<String> getCurrentContext() {
return Optional.ofNullable(SecurityContextHolder.getContext()).map(securityContextSerializer::serialize);
}
@Override
public <T> T runAsTenant(final String tenant, final TenantRunner<T> tenantRunner) {
return runInContext(buildSystemSecurityContext(tenant), tenantRunner);
return runInContext(buildUserSecurityContext(tenant, SYSTEM_USER, SYSTEM_AUTHORITIES), tenantRunner::run);
}
@Override
public <T> T runAsTenantAsUser(final String tenant, final String username, final TenantRunner<T> tenantRunner) {
Objects.requireNonNull(tenant);
Objects.requireNonNull(username);
final List<SimpleGrantedAuthority> authorities = runAsSystem(
() -> authoritiesResolver.getUserAuthorities(tenant, username).stream().map(SimpleGrantedAuthority::new)
.collect(Collectors.toList()));
return runInContext(buildUserSecurityContext(tenant, username, authorities), tenantRunner);
}
@Override
public Optional<String> getCurrentContext() {
return Optional.ofNullable(SecurityContextHolder.getContext()).map(securityContextSerializer::serialize);
return runInContext(buildUserSecurityContext(tenant, username, authorities), tenantRunner::run);
}
@Override
@@ -133,45 +132,35 @@ public class SecurityContextTenantAware implements ContextAware {
final SecurityContext securityContext = securityContextSerializer.deserialize(serializedContext);
Objects.requireNonNull(securityContext);
return runInContext(securityContext, () -> function.apply(t));
}
private static <T> T runInContext(final SecurityContext securityContext, final Supplier<T> supplier) {
final SecurityContext originalContext = SecurityContextHolder.getContext();
if (Objects.equals(securityContext, originalContext)) {
return function.apply(t);
return supplier.get();
} else {
SecurityContextHolder.setContext(securityContext);
try {
return function.apply(t);
return MDCHandler.getInstance().withLoggingRE(supplier::get);
} finally {
SecurityContextHolder.setContext(originalContext);
}
}
}
private static <T> T runInContext(final SecurityContext context, final TenantRunner<T> tenantRunner) {
final SecurityContext originalContext = SecurityContextHolder.getContext();
try {
SecurityContextHolder.setContext(context);
return tenantRunner.run();
} finally {
SecurityContextHolder.setContext(originalContext);
}
}
private static SecurityContext buildSystemSecurityContext(final String tenant) {
return buildUserSecurityContext(tenant, SYSTEM_USER, SYSTEM_AUTHORITIES);
}
private static <T> T runAsSystem(final TenantRunner<T> tenantRunner) {
final SecurityContext currentContext = SecurityContextHolder.getContext();
SystemSecurityContext.setSystemContext(currentContext);
try {
SystemSecurityContext.setSystemContext(currentContext);
return tenantRunner.run();
return MDCHandler.getInstance().withLoggingRE(tenantRunner::run);
} finally {
SecurityContextHolder.setContext(currentContext);
}
}
private static SecurityContext buildUserSecurityContext(final String tenant, final String username,
final Collection<? extends GrantedAuthority> authorities) {
private static SecurityContext buildUserSecurityContext(
final String tenant, final String username, final Collection<? extends GrantedAuthority> authorities) {
final SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new AuthenticationDelegate(
SecurityContextHolder.getContext().getAuthentication(), tenant, username, authorities));
@@ -189,21 +178,25 @@ public class SecurityContextTenantAware implements ContextAware {
private static final long serialVersionUID = 1L;
private final Authentication delegate;
private final TenantAwareUser principal;
private final TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails;
private AuthenticationDelegate(final Authentication delegate, final String tenant, final String username,
final Collection<? extends GrantedAuthority> authorities) {
this.delegate = delegate;
this.principal = new TenantAwareUser(username, username, authorities, tenant);
principal = new TenantAwareUser(username, username, authorities, tenant);
tenantAwareAuthenticationDetails = new TenantAwareAuthenticationDetails(tenant, false);
}
@Override
public boolean equals(final Object another) {
return Objects.equals(delegate, another);
if (another instanceof Authentication anotherAuthentication) {
return Objects.equals(delegate, anotherAuthentication) &&
Objects.equals(principal, anotherAuthentication.getPrincipal()) &&
Objects.equals(tenantAwareAuthenticationDetails, anotherAuthentication.getDetails());
} else {
return false;
}
}
@Override

View File

@@ -107,20 +107,14 @@ public class SystemSecurityContext {
public <T> T runAsSystemAsTenant(final Callable<T> callable, final String tenant) {
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
log.debug("entering system code execution");
log.debug("Entering system code execution");
return tenantAware.runAsTenant(tenant, () -> {
try {
setSystemContext(SecurityContextHolder.getContext());
return MDCHandler.getInstance().withLogging(callable);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new RuntimeException(e);
}
setSystemContext(SecurityContextHolder.getContext());
return MDCHandler.getInstance().withLoggingRE(callable);
});
} finally {
SecurityContextHolder.setContext(oldContext);
log.debug("leaving system code execution");
log.debug("Leaving system code execution");
}
}
@@ -144,12 +138,8 @@ public class SystemSecurityContext {
.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS));
try {
return tenantAware.runAsTenant(tenant, () -> {
try {
setCustomSecurityContext(tenant, oldContext.getAuthentication().getPrincipal(), authorities);
return MDCHandler.getInstance().withLogging(callable);
} catch (final Exception e) {
throw new RuntimeException(e);
}
setCustomSecurityContext(tenant, oldContext.getAuthentication().getPrincipal(), authorities);
return MDCHandler.getInstance().withLoggingRE(callable);
});
} finally {
SecurityContextHolder.setContext(oldContext);