From f555e22ca8938f3d341711d66be4285d72949040 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 7 Nov 2024 19:12:09 +0000 Subject: [PATCH] Improve the way OIDC tenants are grouped and their properties are generated --- .../DefaultPolicyEnforcerResolver.java | 2 +- .../runtime/KeycloakPolicyEnforcerUtil.java | 2 +- .../config/OidcClientCommonConfig.java | 3 +++ .../oidc/deployment/OidcBuildTimeConfig.java | 4 ++- .../quarkus/oidc/test/ProtectedResource.java | 2 +- .../ProtectedResourceWithJwtAccessToken.java | 2 +- ...rotectedResourceWithoutJwtAccessToken.java | 2 +- .../runtime/BackChannelLogoutHandler.java | 2 +- .../io/quarkus/oidc/runtime/OidcConfig.java | 27 ++++++++++++------- .../io/quarkus/oidc/runtime/OidcRecorder.java | 7 +++-- .../oidc/runtime/OidcTenantConfig.java | 12 ++++++++- .../it/keycloak/OidcEventResource.java | 2 +- 12 files changed, 47 insertions(+), 20 deletions(-) diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/DefaultPolicyEnforcerResolver.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/DefaultPolicyEnforcerResolver.java index f7ec6d61f7215..92c560fc6946c 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/DefaultPolicyEnforcerResolver.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/DefaultPolicyEnforcerResolver.java @@ -49,7 +49,7 @@ public class DefaultPolicyEnforcerResolver implements PolicyEnforcerResolver { this.tlsSupport = OidcTlsSupport.empty(); } - var defaultTenantConfig = new OidcTenantConfig(oidcConfig.defaultTenant(), OidcUtils.DEFAULT_TENANT_ID); + var defaultTenantConfig = new OidcTenantConfig(OidcConfig.getDefaultTenant(oidcConfig), OidcUtils.DEFAULT_TENANT_ID); var defaultTenantTlsSupport = tlsSupport.forConfig(defaultTenantConfig.tls); this.defaultPolicyEnforcer = createPolicyEnforcer(defaultTenantConfig, config.defaultTenant(), defaultTenantTlsSupport); diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerUtil.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerUtil.java index 47330c2f01caa..4ee31b741933f 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerUtil.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerUtil.java @@ -226,7 +226,7 @@ private static boolean isNotComplexConfigKey(String key) { static OidcTenantConfig getOidcTenantConfig(OidcConfig oidcConfig, String tenant) { if (tenant == null || DEFAULT_TENANT_ID.equals(tenant)) { - return new OidcTenantConfig(oidcConfig.defaultTenant(), DEFAULT_TENANT_ID); + return new OidcTenantConfig(OidcConfig.getDefaultTenant(oidcConfig), DEFAULT_TENANT_ID); } var oidcTenantConfig = oidcConfig.namedTenants().get(tenant); diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfig.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfig.java index 8ad80d388a927..a795d3f220ad6 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfig.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfig.java @@ -38,6 +38,9 @@ public interface OidcClientCommonConfig extends OidcCommonConfig { */ Credentials credentials(); + /** + * Credentials the OIDC adapter uses to authenticate to the OIDC server. + */ interface Credentials { /** diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildTimeConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildTimeConfig.java index 58cc8d51e3722..2060330618e6c 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildTimeConfig.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildTimeConfig.java @@ -1,6 +1,7 @@ package io.quarkus.oidc.deployment; import io.quarkus.oidc.runtime.OidcConfig; +import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; @@ -18,8 +19,9 @@ public interface OidcBuildTimeConfig { boolean enabled(); /** - * Dev UI configuration. + * OIDC Dev UI configuration which is effective in dev mode only. */ + @ConfigDocSection DevUiConfig devui(); /** diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java index b4dd761333862..c3fd1eb15538b 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java @@ -45,7 +45,7 @@ public void logout() { @Path("access-token-name") @GET public String accessTokenName() { - if (!config.defaultTenant().authentication().verifyAccessToken()) { + if (!OidcConfig.getDefaultTenant(config).authentication().verifyAccessToken()) { throw new IllegalStateException("Access token verification should be enabled"); } return accessToken.getName(); diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java index 59d17dd128ab7..a1adaa128fcce 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java @@ -26,6 +26,6 @@ public class ProtectedResourceWithJwtAccessToken { @GET public String getName() { - return idToken.getName() + ":" + config.defaultTenant().authentication().verifyAccessToken(); + return idToken.getName() + ":" + OidcConfig.getDefaultTenant(config).authentication().verifyAccessToken(); } } diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java index 3305810d605be..8e624842499a7 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java @@ -23,6 +23,6 @@ public class ProtectedResourceWithoutJwtAccessToken { @GET public String getName() { - return idToken.getName() + ":" + config.defaultTenant().authentication().verifyAccessToken(); + return idToken.getName() + ":" + OidcConfig.getDefaultTenant(config).authentication().verifyAccessToken(); } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java index 020a3ba2dc80a..e1679ef7b851a 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java @@ -37,7 +37,7 @@ public BackChannelLogoutHandler(OidcConfig oidcConfig) { } public void setup(@Observes Router router) { - addRoute(router, new OidcTenantConfig(oidcConfig.defaultTenant(), OidcUtils.DEFAULT_TENANT_ID)); + addRoute(router, new OidcTenantConfig(OidcConfig.getDefaultTenant(oidcConfig), OidcUtils.DEFAULT_TENANT_ID)); for (var nameToOidcTenantConfig : oidcConfig.namedTenants().entrySet()) { addRoute(router, new OidcTenantConfig(nameToOidcTenantConfig.getValue(), nameToOidcTenantConfig.getKey())); diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcConfig.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcConfig.java index a92a8ff0f67bf..c0632cb11c887 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcConfig.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcConfig.java @@ -10,31 +10,31 @@ import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; +import io.smallrye.config.WithDefaults; import io.smallrye.config.WithParentName; +import io.smallrye.config.WithUnnamedKey; @ConfigMapping(prefix = "quarkus.oidc") @ConfigRoot(phase = ConfigPhase.RUN_TIME) public interface OidcConfig { - /** - * The default tenant. - */ - @WithParentName - OidcTenantConfig defaultTenant(); + String DEFAULT_TENANT_KEY = ""; /** * Additional named tenants. */ - @ConfigDocSection @ConfigDocMapKey("tenant") @WithParentName + @WithUnnamedKey(DEFAULT_TENANT_KEY) + @WithDefaults Map namedTenants(); /** - * Default TokenIntrospection and UserInfo Cache configuration which is used for all the tenants if it is enabled - * with the build-time 'quarkus.oidc.default-token-cache-enabled' property ('true' by default) and also activated, - * see its `max-size` property. + * Default TokenIntrospection and UserInfo Cache configuration. + * It is used for all the tenants if it is enabled with the build-time 'quarkus.oidc.default-token-cache-enabled' property + * ('true' by default) and also activated, see its `max-size` property. */ + @ConfigDocSection TokenCache tokenCache(); /** @@ -66,4 +66,13 @@ interface TokenCache { */ Optional cleanUpTimerInterval(); } + + static io.quarkus.oidc.runtime.OidcTenantConfig getDefaultTenant(OidcConfig config) { + for (var tenant : config.namedTenants().entrySet()) { + if (OidcConfig.DEFAULT_TENANT_KEY.equals(tenant.getKey())) { + return tenant.getValue(); + } + } + return null; + } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index 9dd8de738e6ee..086148a1eb96f 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -111,7 +111,7 @@ public TenantConfigBean setup(OidcConfig config, Vertx vertxValue, OidcTlsSuppor boolean userInfoInjectionPointDetected) { OidcRecorder.userInfoInjectionPointDetected = userInfoInjectionPointDetected; - var defaultTenant = new OidcTenantConfig(config.defaultTenant(), DEFAULT_TENANT_ID); + var defaultTenant = new OidcTenantConfig(OidcConfig.getDefaultTenant(config), DEFAULT_TENANT_ID); String defaultTenantId = defaultTenant.getTenantId().get(); var defaultTenantInitializer = createStaticTenantContextCreator(vertxValue, defaultTenant, !config.namedTenants().isEmpty(), defaultTenantId, tlsSupport); @@ -120,6 +120,9 @@ public TenantConfigBean setup(OidcConfig config, Vertx vertxValue, OidcTlsSuppor Map staticTenantsConfig = new HashMap<>(); for (var tenant : config.namedTenants().entrySet()) { + if (OidcConfig.DEFAULT_TENANT_KEY.equals(tenant.getKey())) { + continue; + } var namedTenantConfig = new OidcTenantConfig(tenant.getValue(), tenant.getKey()); OidcCommonUtils.verifyConfigurationId(defaultTenantId, tenant.getKey(), namedTenantConfig.getTenantId()); var staticTenantInitializer = createStaticTenantContextCreator(vertxValue, namedTenantConfig, false, @@ -709,7 +712,7 @@ private TenantSpecificOidcIdentityProvider(String tenantId) { this.blockingExecutor = Arc.container().instance(BlockingSecurityExecutor.class).get(); if (tenantId.equals(DEFAULT_TENANT_ID)) { OidcConfig config = Arc.container().instance(OidcConfig.class).get(); - this.tenantId = config.defaultTenant().tenantId().orElse(OidcUtils.DEFAULT_TENANT_ID); + this.tenantId = OidcConfig.getDefaultTenant(config).tenantId().orElse(OidcUtils.DEFAULT_TENANT_ID); } else { this.tenantId = tenantId; } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTenantConfig.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTenantConfig.java index bd34006639614..f7e045e725a60 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTenantConfig.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTenantConfig.java @@ -16,6 +16,7 @@ import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.runtime.annotations.ConfigDocDefault; import io.quarkus.runtime.annotations.ConfigDocMapKey; +import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.configuration.TrimmedStringConverter; import io.quarkus.security.identity.SecurityIdentityAugmentor; import io.smallrye.config.WithConverter; @@ -103,10 +104,12 @@ public interface OidcTenantConfig extends OidcClientCommonConfig { Optional publicKey(); /** - * Introspection Basic Authentication which must be configured only if the introspection is required + * Introspection Basic authentication. + * It must be configured only if the introspection is required * and OpenId Connect Provider does not support the OIDC client authentication configured with * {@link OidcCommonConfig#credentials} for its introspection endpoint. */ + @ConfigDocSection IntrospectionCredentials introspectionCredentials(); /** @@ -134,16 +137,19 @@ interface IntrospectionCredentials { /** * Configuration to find and parse a custom claim containing the roles information. */ + @ConfigDocSection Roles roles(); /** * Configuration how to validate the token claims. */ + @ConfigDocSection Token token(); /** * RP Initiated, BackChannel and FrontChannel Logout configuration */ + @ConfigDocSection Logout logout(); /** @@ -161,6 +167,7 @@ interface IntrospectionCredentials { * If the truststore does not have the leaf certificate imported, then the leaf certificate must be identified by its Common * Name. */ + @ConfigDocSection CertificateChain certificateChain(); interface CertificateChain { @@ -198,16 +205,19 @@ interface CertificateChain { /** * Different options to configure authorization requests */ + @ConfigDocSection Authentication authentication(); /** * Authorization code grant configuration */ + @ConfigDocSection CodeGrant codeGrant(); /** * Default token state manager configuration */ + @ConfigDocSection TokenStateManager tokenStateManager(); /** diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcEventResource.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcEventResource.java index 7a888ee8217e0..43292799e4805 100644 --- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcEventResource.java +++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcEventResource.java @@ -16,7 +16,7 @@ public class OidcEventResource { private final String expectedAuthServerUrl; public OidcEventResource(OidcEventObserver oidcEventObserver, OidcConfig oidcConfig) { - this.expectedAuthServerUrl = dropTrailingSlash(oidcConfig.defaultTenant().authServerUrl().get()); + this.expectedAuthServerUrl = dropTrailingSlash(OidcConfig.getDefaultTenant(oidcConfig).authServerUrl().get()); this.oidcEventObserver = oidcEventObserver; }