diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java index 45b91d7b6..4efc9007e 100644 --- a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java +++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java @@ -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 } - - } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java index b766c71fb..a8bda5e51 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java @@ -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); + } + } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/multitenacy/HeaderBasedKeycloakRealmResolver.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/multitenacy/HeaderBasedKeycloakRealmResolver.java new file mode 100644 index 000000000..8523ed9a3 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/multitenacy/HeaderBasedKeycloakRealmResolver.java @@ -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 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 values = request.getHeaders(headerName); + if (values == null || values.isEmpty()) { + return null; + } + return values.get(values.size() - 1); + } + +} \ No newline at end of file