Introduce pluggable tenant resolver (#2151)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -16,9 +16,10 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware.DefaultTenantResolver;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware.TenantResolver;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties.User;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.InMemoryUserAuthoritiesResolver;
|
||||
import org.eclipse.hawkbit.security.MdcHandler;
|
||||
@@ -56,6 +57,12 @@ import org.springframework.util.CollectionUtils;
|
||||
@EnableConfigurationProperties({ SecurityProperties.class, HawkbitSecurityProperties.class, TenantAwareUserProperties.class })
|
||||
public class SecurityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TenantResolver tenantResolver() {
|
||||
return new DefaultTenantResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ContextAware} (hence {@link TenantAware}) bean based on the given {@link UserAuthoritiesResolver} and
|
||||
* {@link SecurityContextSerializer}.
|
||||
@@ -68,8 +75,9 @@ public class SecurityAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
public ContextAware contextAware(
|
||||
final UserAuthoritiesResolver authoritiesResolver,
|
||||
@Autowired(required = false) final SecurityContextSerializer securityContextSerializer) {
|
||||
return new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer);
|
||||
@Autowired(required = false) final SecurityContextSerializer securityContextSerializer,
|
||||
@Autowired(required = false) final TenantResolver tenantResolver) {
|
||||
return new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer, tenantResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.tenancy;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* Interface for components that are aware of the application's current tenant.
|
||||
*/
|
||||
@@ -66,4 +69,29 @@ public interface TenantAware {
|
||||
*/
|
||||
T run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the tenant from the current context.
|
||||
*/
|
||||
interface TenantResolver {
|
||||
|
||||
String resolveTenant();
|
||||
}
|
||||
|
||||
class DefaultTenantResolver implements TenantResolver {
|
||||
|
||||
@Override
|
||||
public String resolveTenant() {
|
||||
final SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context.getAuthentication() != null) {
|
||||
final Object principal = context.getAuthentication().getPrincipal();
|
||||
if (context.getAuthentication().getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails) {
|
||||
return tenantAwareAuthenticationDetails.getTenant();
|
||||
} else if (principal instanceof TenantAwareUser tenantAwareUser) {
|
||||
return tenantAwareUser.getTenant();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,9 +49,9 @@ public class JpaConfiguration extends JpaBaseConfiguration {
|
||||
protected JpaConfiguration(
|
||||
final DataSource dataSource, final JpaProperties properties,
|
||||
final ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider,
|
||||
final TenantAware tenantAware) {
|
||||
final TenantAware.TenantResolver tenantResolver) {
|
||||
super(dataSource, properties, jtaTransactionManagerProvider);
|
||||
tenantIdentifier = new TenantIdentifier(tenantAware);
|
||||
tenantIdentifier = new TenantIdentifier(tenantResolver);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -21,16 +21,16 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomi
|
||||
*/
|
||||
class TenantIdentifier implements CurrentTenantIdentifierResolver<String> {
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
private final TenantAware.TenantResolver tenantResolver;
|
||||
|
||||
TenantIdentifier(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
TenantIdentifier(final TenantAware.TenantResolver tenantResolver) {
|
||||
this.tenantResolver = tenantResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
// on bootstrapping hibernate requests tenant and want to be non-null
|
||||
return Optional.ofNullable(tenantAware.getCurrentTenant()).map(String::toUpperCase).orElse("");
|
||||
return Optional.ofNullable(tenantResolver.resolveTenant()).map(String::toUpperCase).orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
<id>eclipselink</id>
|
||||
<activation>
|
||||
<property>
|
||||
<!-- default, if not set - eclipse link -->
|
||||
<name>!jpa.vendor</name>
|
||||
<!-- default, if not set (or not hibernate) - eclipse link -->
|
||||
<name>jpa.vendor</name>
|
||||
<value>!hibernate</value>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware.DefaultTenantResolver;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware.TenantResolver;
|
||||
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
@@ -165,9 +167,16 @@ public class TestConfiguration implements AsyncConfigurer {
|
||||
}
|
||||
|
||||
@Bean
|
||||
ContextAware contextAware(final UserAuthoritiesResolver authoritiesResolver, final SecurityContextSerializer securityContextSerializer) {
|
||||
TenantResolver tenantResolver() {
|
||||
return new DefaultTenantResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ContextAware contextAware(
|
||||
final UserAuthoritiesResolver authoritiesResolver, final SecurityContextSerializer securityContextSerializer,
|
||||
final TenantResolver tenantResolver) {
|
||||
// allow spying the security context
|
||||
return org.mockito.Mockito.spy(new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer));
|
||||
return org.mockito.Mockito.spy(new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer, tenantResolver));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -161,8 +161,8 @@ public class RestConfiguration {
|
||||
logRequest(request, ex);
|
||||
final ExceptionInfo response = createExceptionInfo(ex);
|
||||
final HttpStatus responseStatus;
|
||||
if (ex instanceof AbstractServerRtException) {
|
||||
responseStatus = getStatusOrDefault(((AbstractServerRtException) ex).getError());
|
||||
if (ex instanceof AbstractServerRtException abstractServerRtException) {
|
||||
responseStatus = getStatusOrDefault(abstractServerRtException.getError());
|
||||
} else {
|
||||
responseStatus = DEFAULT_RESPONSE_STATUS;
|
||||
}
|
||||
@@ -278,16 +278,20 @@ public class RestConfiguration {
|
||||
}
|
||||
|
||||
private void logRequest(final HttpServletRequest request, final Exception ex) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL(), ex);
|
||||
} else {
|
||||
log.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
|
||||
}
|
||||
}
|
||||
|
||||
private ExceptionInfo createExceptionInfo(final Exception ex) {
|
||||
final ExceptionInfo response = new ExceptionInfo();
|
||||
response.setMessage(ex.getMessage());
|
||||
response.setExceptionClass(ex.getClass().getName());
|
||||
if (ex instanceof AbstractServerRtException) {
|
||||
response.setErrorCode(((AbstractServerRtException) ex).getError().getKey());
|
||||
response.setInfo(((AbstractServerRtException) ex).getInfo());
|
||||
if (ex instanceof AbstractServerRtException abstractServerRtException) {
|
||||
response.setErrorCode(abstractServerRtException.getError().getKey());
|
||||
response.setInfo(abstractServerRtException.getInfo());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -305,9 +309,6 @@ public class RestConfiguration {
|
||||
private final String[] excludeAntPaths;
|
||||
private final AntPathMatcher antMatcher = new AntPathMatcher();
|
||||
|
||||
/**
|
||||
* @param excludeAntPaths
|
||||
*/
|
||||
public ExcludePathAwareShallowETagFilter(final String... excludeAntPaths) {
|
||||
this.excludeAntPaths = excludeAntPaths;
|
||||
}
|
||||
|
||||
@@ -43,20 +43,19 @@ 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));
|
||||
List.of(new SimpleGrantedAuthority(SpringEvalExpressions.SYSTEM_ROLE));
|
||||
|
||||
private final UserAuthoritiesResolver authoritiesResolver;
|
||||
private final SecurityContextSerializer securityContextSerializer;
|
||||
private final TenantResolver tenantResolver;
|
||||
|
||||
/**
|
||||
* 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 authoritiesResolver Resolver to retrieve the authorities for a given user. Must not be <code>null</code>..
|
||||
*/
|
||||
public SecurityContextTenantAware(final UserAuthoritiesResolver authoritiesResolver) {
|
||||
this.authoritiesResolver = authoritiesResolver;
|
||||
this.securityContextSerializer = SecurityContextSerializer.NOP;
|
||||
this(authoritiesResolver, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,24 +64,30 @@ public class SecurityContextTenantAware implements ContextAware {
|
||||
* @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,
|
||||
public SecurityContextTenantAware(
|
||||
final UserAuthoritiesResolver authoritiesResolver,
|
||||
@Nullable final SecurityContextSerializer securityContextSerializer) {
|
||||
this(authoritiesResolver, securityContextSerializer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public SecurityContextTenantAware(
|
||||
final UserAuthoritiesResolver authoritiesResolver,
|
||||
@Nullable final SecurityContextSerializer securityContextSerializer,
|
||||
@Nullable final TenantResolver tenantResolver) {
|
||||
this.authoritiesResolver = authoritiesResolver;
|
||||
this.securityContextSerializer = securityContextSerializer == null ? SecurityContextSerializer.NOP : securityContextSerializer;
|
||||
this.tenantResolver = tenantResolver == null ? new DefaultTenantResolver() : tenantResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentTenant() {
|
||||
final SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context.getAuthentication() != null) {
|
||||
final Object principal = context.getAuthentication().getPrincipal();
|
||||
if (context.getAuthentication().getDetails() instanceof TenantAwareAuthenticationDetails) {
|
||||
return ((TenantAwareAuthenticationDetails) context.getAuthentication().getDetails()).getTenant();
|
||||
} else if (principal instanceof TenantAwareUser) {
|
||||
return ((TenantAwareUser) principal).getTenant();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return tenantResolver.resolveTenant();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,11 +95,11 @@ public class SecurityContextTenantAware implements ContextAware {
|
||||
final SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context.getAuthentication() != null) {
|
||||
final Object principal = context.getAuthentication().getPrincipal();
|
||||
if (principal instanceof OidcUser) {
|
||||
return ((OidcUser) principal).getPreferredUsername();
|
||||
if (principal instanceof OidcUser oidcUser) {
|
||||
return oidcUser.getPreferredUsername();
|
||||
}
|
||||
if (principal instanceof User) {
|
||||
return ((User) principal).getUsername();
|
||||
if (principal instanceof User user) {
|
||||
return user.getUsername();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user