RED-4515: Intercept keycloak authentication and choose realm from tenant header

This commit is contained in:
deiflaender 2023-03-22 08:54:58 +01:00
parent 112a27561b
commit be364e2c6d
3 changed files with 91 additions and 8 deletions

View File

@ -43,8 +43,10 @@ import lombok.RequiredArgsConstructor;
@EnableConfigurationProperties(KeyCloakSettings.class)
@Import(KeycloakSpringBootConfigResolver.class)
public class SecuredKeyCloakConfiguration extends KeycloakWebSecurityConfigurerAdapter {
private final KeyCloakSettings keyCloakSettings;
@Bean
public KeycloakBaseSpringBootConfiguration keycloakBaseSpringBootConfiguration() {
@ -67,12 +69,13 @@ public class SecuredKeyCloakConfiguration extends KeycloakWebSecurityConfigurerA
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/actuator/health/**",
"/redaction-gateway-v1/async/download/with-ott/**",
"/redaction-gateway-v1/docs/**",
"/redaction-gateway-v1/docs",
"/redaction-gateway-v1",
"/internal-api/**");
web.ignoring()
.antMatchers("/actuator/health/**",
"/redaction-gateway-v1/async/download/with-ott/**",
"/redaction-gateway-v1/docs/**",
"/redaction-gateway-v1/docs",
"/redaction-gateway-v1",
"/internal-api/**");
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@ -84,6 +87,7 @@ public class SecuredKeyCloakConfiguration extends KeycloakWebSecurityConfigurerA
protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean());
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
filter.setRequestAuthenticatorFactory(new SpringSecurityRequestAuthenticatorFactory() {
@ -142,6 +146,4 @@ public class SecuredKeyCloakConfiguration extends KeycloakWebSecurityConfigurerA
}
}

View File

@ -1,11 +1,26 @@
package com.iqser.red.persistence.service.v1.external.api.impl;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.iqser.red.persistence.service.v1.external.api.impl.multitenacy.HeaderBasedKeycloakRealmResolver;
@Configuration
@ComponentScan
public class PersistenceServiceExternalApiConfiguration {
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new HeaderBasedKeycloakRealmResolver();
}
@Autowired
public void setKeycloakSpringBootProperties(final KeycloakSpringBootProperties keycloakProperties) {
HeaderBasedKeycloakRealmResolver.setAdapterConfig(keycloakProperties);
}
}

View File

@ -0,0 +1,66 @@
package com.iqser.red.persistence.service.v1.external.api.impl.multitenacy;
import static com.iqser.red.service.persistence.management.v1.processor.multitenancy.TenantInterceptor.TENANT_HEADER_NAME;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.representations.adapters.config.AdapterConfig;
import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class HeaderBasedKeycloakRealmResolver implements KeycloakConfigResolver {
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<>();
@Setter
private static AdapterConfig adapterConfig;
private KeycloakDeployment defaultDeployment;
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String tenant = getHeader(request, TENANT_HEADER_NAME);
if (tenant == null) {
if (defaultDeployment == null) {
defaultDeployment = KeycloakDeploymentBuilder.build(adapterConfig);
}
return defaultDeployment;
}
return cache.computeIfAbsent(tenant, this::createKeyCloakDeployment);
}
private KeycloakDeployment createKeyCloakDeployment(String tenant) {
var config = MagicConverter.convert(adapterConfig, AdapterConfig.class);
config.setRealm(tenant);
return KeycloakDeploymentBuilder.build(config);
}
private String getHeader(HttpFacade.Request request, String headerName) {
List<String> values = request.getHeaders(headerName);
if (values == null || values.isEmpty()) {
return null;
}
return values.get(values.size() - 1);
}
}