diff --git a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc index 329345aa1a95a..1f82509661f6b 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -355,8 +355,8 @@ import java.util.Map; import org.eclipse.microprofile.config.inject.ConfigProperty; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig; -import io.quarkus.oidc.client.OidcClientConfig.Grant.Type; +import io.quarkus.oidc.client.runtime.OidcClientConfig; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant.Type; import io.quarkus.oidc.client.OidcClients; import io.quarkus.runtime.StartupEvent; import io.smallrye.mutiny.Uni; @@ -383,14 +383,14 @@ public class OidcClientCreator { } private Uni createOidcClient() { - OidcClientConfig cfg = new OidcClientConfig(); - cfg.setId("myclient"); - cfg.setAuthServerUrl(oidcProviderAddress); - cfg.setClientId("backend-service"); - cfg.getCredentials().setSecret("secret"); - cfg.getGrant().setType(Type.PASSWORD); - cfg.setGrantOptions(Map.of("password", - Map.of("username", "alice", "password", "alice"))); + OidcClientConfig cfg = OidcClientConfig + .authServerUrl(oidcProviderAddress) + .id("myclient") + .clientId("backend-service") + .credentials("secret") + .grant(Type.PASSWORD) + .grantOptions("password", Map.of("username", "alice", "password", "alice")) + .build(); return oidcClients.newClient(cfg); } } diff --git a/docs/src/main/asciidoc/security-openid-connect-client.adoc b/docs/src/main/asciidoc/security-openid-connect-client.adoc index 134a70af24368..5444790c3f762 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client.adoc @@ -303,14 +303,14 @@ public class OidcClientCreator { } private Uni createOidcClient() { - OidcClientConfig cfg = new OidcClientConfig(); - cfg.setId("myclient"); - cfg.setAuthServerUrl(oidcProviderAddress); - cfg.setClientId("backend-service"); - cfg.getCredentials().setSecret("secret"); - cfg.getGrant().setType(Type.PASSWORD); - cfg.setGrantOptions(Map.of("password", - Map.of("username", "alice", "password", "alice"))); + OidcClientConfig cfg = OidcClientConfig + .authServerUrl(oidcProviderAddress) + .id("myclient") + .clientId("backend-service") + .credentials("secret") + .grant(Type.PASSWORD) + .grantOptions("password", Map.of("username", "alice", "password", "alice")) + .build(); return oidcClients.newClient(cfg); } } 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 92c560fc6946c..528353d22127e 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 @@ -50,7 +50,7 @@ public class DefaultPolicyEnforcerResolver implements PolicyEnforcerResolver { } var defaultTenantConfig = new OidcTenantConfig(OidcConfig.getDefaultTenant(oidcConfig), OidcUtils.DEFAULT_TENANT_ID); - var defaultTenantTlsSupport = tlsSupport.forConfig(defaultTenantConfig.tls); + var defaultTenantTlsSupport = tlsSupport.forConfig(defaultTenantConfig.tls()); this.defaultPolicyEnforcer = createPolicyEnforcer(defaultTenantConfig, config.defaultTenant(), defaultTenantTlsSupport); this.namedPolicyEnforcers = createNamedPolicyEnforcers(oidcConfig, config, tlsSupport); @@ -101,7 +101,7 @@ private Uni getDynamicPolicyEnforcer(RoutingContext routingConte .onItem().ifNotNull().transform(new Function() { @Override public PolicyEnforcer apply(KeycloakPolicyEnforcerTenantConfig tenant) { - return createPolicyEnforcer(config, tenant, tlsSupport.forConfig(config.tls)); + return createPolicyEnforcer(config, tenant, tlsSupport.forConfig(config.tls())); } }); } @@ -116,7 +116,7 @@ private static Map createNamedPolicyEnforcers(OidcConfig for (Map.Entry tenant : config.namedTenants().entrySet()) { OidcTenantConfig oidcTenantConfig = getOidcTenantConfig(oidcConfig, tenant.getKey()); policyEnforcerTenants.put(tenant.getKey(), - createPolicyEnforcer(oidcTenantConfig, tenant.getValue(), tlsSupport.forConfig(oidcTenantConfig.tls))); + createPolicyEnforcer(oidcTenantConfig, tenant.getValue(), tlsSupport.forConfig(oidcTenantConfig.tls()))); } return Map.copyOf(policyEnforcerTenants); } 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 4ee31b741933f..b52b598e051d0 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 @@ -18,8 +18,8 @@ import io.quarkus.oidc.OIDCException; import io.quarkus.oidc.OidcTenantConfig; -import io.quarkus.oidc.common.runtime.OidcCommonConfig; import io.quarkus.oidc.common.runtime.OidcTlsSupport.TlsConfigSupport; +import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.oidc.runtime.OidcConfig; import io.quarkus.runtime.configuration.ConfigurationException; @@ -53,16 +53,16 @@ static PolicyEnforcer createPolicyEnforcer(OidcTenantConfig oidcConfig, adapterConfig.setCredentials(getCredentials(oidcConfig)); if (!tlsConfigSupport.useTlsRegistry()) { - boolean trustAll = oidcConfig.tls.getVerification().isPresent() - ? oidcConfig.tls.getVerification().get() == OidcCommonConfig.Tls.Verification.NONE + boolean trustAll = oidcConfig.tls().verification().isPresent() + ? oidcConfig.tls().verification().get() == OidcCommonConfig.Tls.Verification.NONE : tlsConfigSupport.isGlobalTrustAll(); if (trustAll) { adapterConfig.setDisableTrustManager(true); adapterConfig.setAllowAnyHostname(true); - } else if (oidcConfig.tls.trustStoreFile.isPresent()) { - adapterConfig.setTruststore(oidcConfig.tls.trustStoreFile.get().toString()); - adapterConfig.setTruststorePassword(oidcConfig.tls.trustStorePassword.orElse("password")); - if (OidcCommonConfig.Tls.Verification.CERTIFICATE_VALIDATION == oidcConfig.tls.verification + } else if (oidcConfig.tls().trustStoreFile().isPresent()) { + adapterConfig.setTruststore(oidcConfig.tls().trustStoreFile().get().toString()); + adapterConfig.setTruststorePassword(oidcConfig.tls().trustStorePassword().orElse("password")); + if (OidcCommonConfig.Tls.Verification.CERTIFICATE_VALIDATION == oidcConfig.tls().verification() .orElse(OidcCommonConfig.Tls.Verification.REQUIRED)) { adapterConfig.setAllowAnyHostname(true); } diff --git a/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java b/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java index 5e2bb4d70c012..2d3b124aab56e 100644 --- a/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java +++ b/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java @@ -34,6 +34,7 @@ import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassOutput; @@ -46,6 +47,7 @@ import io.quarkus.oidc.client.Tokens; import io.quarkus.oidc.client.runtime.AbstractTokensProducer; import io.quarkus.oidc.client.runtime.OidcClientBuildTimeConfig; +import io.quarkus.oidc.client.runtime.OidcClientDefaultIdConfigBuilder; import io.quarkus.oidc.client.runtime.OidcClientRecorder; import io.quarkus.oidc.client.runtime.OidcClientsConfig; import io.quarkus.oidc.client.runtime.TokenProviderProducer; @@ -184,6 +186,11 @@ private AccessTokenInstanceBuildItem build() { return index.getIndex().getAnnotations(ACCESS_TOKEN).stream().map(ItemBuilder::new).map(ItemBuilder::build).toList(); } + @BuildStep + RunTimeConfigBuilderBuildItem useOidcClientDefaultIdConfigBuilder() { + return new RunTimeConfigBuilderBuildItem(OidcClientDefaultIdConfigBuilder.class); + } + /** * Creates a Tokens producer class like follows: * diff --git a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientKeycloakDevServiceTest.java b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientKeycloakDevServiceTest.java index b5f64ce0343ad..8a2e94cb42caa 100644 --- a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientKeycloakDevServiceTest.java +++ b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientKeycloakDevServiceTest.java @@ -5,10 +5,14 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.oidc.client.runtime.OidcClientsConfig; import io.quarkus.oidc.runtime.OidcUtils; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; @@ -25,6 +29,23 @@ public class OidcClientKeycloakDevServiceTest { .addClasses(NamedOidcClientResource.class) .addAsResource("oidc-client-dev-service-test.properties", "application.properties")); + @Inject + OidcClientsConfig config; + + @Test + public void testOidcClientDefaultIdIsSet() { + var defaultClientConfig = OidcClientsConfig.getDefaultClient(config); + // not set, so "Default" id should be set by Quarkus + assertEquals("Default", defaultClientConfig.id().orElse(null)); + // not set, so named key "client1" should be set by Quarkus + assertEquals("client1", config.namedClients().get("client1").id().orElse(null)); + // set to "client2" in application.properties + // we cannot set here any different value, because ATM OIDC client enforce that ID always equal named key anyway + assertEquals("client2", config.namedClients().get("client2").id().orElse(null)); + // not set and named key "client3" is enclosed in double quotes, so named key "client3" should be set by Quarkus + assertEquals("client3", config.namedClients().get("client3").id().orElse(null)); + } + @Test public void testInjectedNamedOidcClients() { String token1 = doTestGetTokenByNamedClient("client1"); diff --git a/extensions/oidc-client/deployment/src/test/resources/oidc-client-dev-service-test.properties b/extensions/oidc-client/deployment/src/test/resources/oidc-client-dev-service-test.properties index 51063eea9940c..e64f210702d10 100644 --- a/extensions/oidc-client/deployment/src/test/resources/oidc-client-dev-service-test.properties +++ b/extensions/oidc-client/deployment/src/test/resources/oidc-client-dev-service-test.properties @@ -13,3 +13,11 @@ quarkus.oidc-client.client2.credentials.secret=${quarkus.oidc-client.credentials quarkus.oidc-client.client2.grant.type=password quarkus.oidc-client.client2.grant-options.password.username=bob quarkus.oidc-client.client2.grant-options.password.password=bob +quarkus.oidc-client.client2.id=client2 + +quarkus.oidc-client."client3".auth-server-url=${quarkus.oidc-client.auth-server-url} +quarkus.oidc-client."client3".client-id=${quarkus.oidc-client.client-id} +quarkus.oidc-client."client3".credentials.secret=${quarkus.oidc-client.credentials.secret} +quarkus.oidc-client."client3".grant.type=password +quarkus.oidc-client."client3".grant-options.password.username=bob +quarkus.oidc-client."client3".grant-options.password.password=bob diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java index 0d15cb4e9f0c1..e0eec17e533d6 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java @@ -8,7 +8,12 @@ import io.quarkus.oidc.common.runtime.OidcClientCommonConfig; import io.quarkus.oidc.common.runtime.OidcConstants; -public class OidcClientConfig extends OidcClientCommonConfig { +/** + * @deprecated create {@link io.quarkus.oidc.client.runtime.OidcClientConfig} with the {@link OidcClientConfigBuilder} + * for example, you can use the {@link io.quarkus.oidc.client.runtime.OidcClientConfig#builder()} method. + */ +@Deprecated(since = "3.18") +public class OidcClientConfig extends OidcClientCommonConfig implements io.quarkus.oidc.client.runtime.OidcClientConfig { public OidcClientConfig() { @@ -67,7 +72,82 @@ public OidcClientConfig(io.quarkus.oidc.client.runtime.OidcClientConfig mapping) public Grant grant = new Grant(); - public static class Grant { + @Override + public Optional id() { + return id; + } + + @Override + public boolean clientEnabled() { + return clientEnabled; + } + + @Override + public Optional> scopes() { + return scopes; + } + + @Override + public Optional refreshTokenTimeSkew() { + return refreshTokenTimeSkew; + } + + @Override + public Optional accessTokenExpiresIn() { + return accessTokenExpiresIn; + } + + @Override + public boolean absoluteExpiresIn() { + return absoluteExpiresIn; + } + + @Override + public io.quarkus.oidc.client.runtime.OidcClientConfig.Grant grant() { + return grant; + } + + @Override + public Map> grantOptions() { + return grantOptions; + } + + @Override + public boolean earlyTokensAcquisition() { + return earlyTokensAcquisition; + } + + @Override + public Map headers() { + return headers; + } + + public static class Grant implements io.quarkus.oidc.client.runtime.OidcClientConfig.Grant { + + @Override + public io.quarkus.oidc.client.runtime.OidcClientConfig.Grant.Type type() { + return type == null ? null : io.quarkus.oidc.client.runtime.OidcClientConfig.Grant.Type.valueOf(type.toString()); + } + + @Override + public String accessTokenProperty() { + return accessTokenProperty; + } + + @Override + public String refreshTokenProperty() { + return refreshTokenProperty; + } + + @Override + public String expiresInProperty() { + return expiresInProperty; + } + + @Override + public String refreshExpiresInProperty() { + return refreshExpiresInProperty; + } public static enum Type { /** diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfigBuilder.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfigBuilder.java new file mode 100644 index 0000000000000..6e30ad6178773 --- /dev/null +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfigBuilder.java @@ -0,0 +1,390 @@ +package io.quarkus.oidc.client; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import io.quarkus.oidc.client.runtime.OidcClientConfig; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfigBuilder; + +/** + * Builder for the {@link io.quarkus.oidc.client.runtime.OidcClientConfig}. This builder is not thread-safe. + */ +public final class OidcClientConfigBuilder extends OidcClientCommonConfigBuilder { + + private static class OidcClientConfigImpl extends OidcClientCommonConfigImpl implements OidcClientConfig { + + private final Map headers; + private final boolean earlyTokensAcquisition; + private final Map> grantOptions; + private final Grant grant; + private final boolean absoluteExpiresIn; + private final Optional accessTokenExpiresIn; + private final Optional refreshTokenTimeSkew; + private final Optional> scopes; + private final boolean clientEnabled; + private final Optional id; + + private OidcClientConfigImpl(OidcClientConfigBuilder builder) { + super(builder); + this.headers = Map.copyOf(builder.headers); + this.earlyTokensAcquisition = builder.earlyTokensAcquisition; + this.grantOptions = Map.copyOf(builder.grantOptions); + this.grant = builder.grant; + this.absoluteExpiresIn = builder.absoluteExpiresIn; + this.accessTokenExpiresIn = builder.accessTokenExpiresIn; + this.refreshTokenTimeSkew = builder.refreshTokenTimeSkew; + this.scopes = builder.scopes.isEmpty() ? Optional.empty() : Optional.of(List.copyOf(builder.scopes)); + this.clientEnabled = builder.clientEnabled; + this.id = builder.id; + } + + @Override + public Optional id() { + return id; + } + + @Override + public boolean clientEnabled() { + return clientEnabled; + } + + @Override + public Optional> scopes() { + return scopes; + } + + @Override + public Optional refreshTokenTimeSkew() { + return refreshTokenTimeSkew; + } + + @Override + public Optional accessTokenExpiresIn() { + return accessTokenExpiresIn; + } + + @Override + public boolean absoluteExpiresIn() { + return absoluteExpiresIn; + } + + @Override + public Grant grant() { + return grant; + } + + @Override + public Map> grantOptions() { + return grantOptions; + } + + @Override + public boolean earlyTokensAcquisition() { + return earlyTokensAcquisition; + } + + @Override + public Map headers() { + return headers; + } + } + + private final Map headers = new HashMap<>(); + private boolean earlyTokensAcquisition; + private final Map> grantOptions = new HashMap<>(); + private final List scopes = new ArrayList<>(); + private Grant grant; + private boolean absoluteExpiresIn; + private Optional accessTokenExpiresIn; + private Optional refreshTokenTimeSkew; + private boolean clientEnabled; + private Optional id; + + /** + * @param config created either by this builder or SmallRye Config; config methods must never return null + */ + public OidcClientConfigBuilder(OidcClientConfig config) { + super(Objects.requireNonNull(config)); + this.headers.putAll(config.headers()); + this.earlyTokensAcquisition = config.earlyTokensAcquisition(); + this.grantOptions.putAll(config.grantOptions()); + this.grant = config.grant(); + this.absoluteExpiresIn = config.absoluteExpiresIn(); + this.accessTokenExpiresIn = config.accessTokenExpiresIn(); + this.refreshTokenTimeSkew = config.refreshTokenTimeSkew(); + this.clientEnabled = config.clientEnabled(); + this.id = config.id(); + if (config.scopes().isPresent()) { + this.scopes.addAll(config.scopes().get()); + } + } + + @Override + protected OidcClientConfigBuilder getBuilder() { + return this; + } + + /** + * Adds new headers to the {@link OidcClientConfig#headers()} already set. + * + * @param headerName header name + * @param headerValue header value + * @return this builder + */ + public OidcClientConfigBuilder headers(String headerName, String headerValue) { + Objects.requireNonNull(headerName); + Objects.requireNonNull(headerValue); + this.headers.put(headerName, headerValue); + return this; + } + + /** + * Adds new headers to the headers already set. + * + * @param headers {@link OidcClientConfig#headers()} + * @return this builder + */ + public OidcClientConfigBuilder headers(Map headers) { + Objects.requireNonNull(headers); + this.headers.putAll(headers); + return this; + } + + /** + * @param earlyTokensAcquisition {@link OidcClientConfig#earlyTokensAcquisition()} + * @return this builder + */ + public OidcClientConfigBuilder earlyTokensAcquisition(boolean earlyTokensAcquisition) { + this.earlyTokensAcquisition = earlyTokensAcquisition; + return this; + } + + /** + * Adds {@link OidcClientConfig#grantOptions()}. + * + * @return this builder + */ + public OidcClientConfigBuilder grantOptions(String grantName, Map options) { + Objects.requireNonNull(grantName); + Objects.requireNonNull(options); + this.grantOptions.computeIfAbsent(grantName, k -> new HashMap<>()).putAll(options); + return this; + } + + /** + * Adds {@link OidcClientConfig#grantOptions()}. + * + * @return this builder + */ + public OidcClientConfigBuilder grantOptions(String grantName, String key, String value) { + Objects.requireNonNull(grantName); + Objects.requireNonNull(key); + Objects.requireNonNull(value); + this.grantOptions.computeIfAbsent(grantName, k -> new HashMap<>()).put(key, value); + return this; + } + + /** + * @param grantOptions {@link OidcClientConfig#grantOptions()} + * @return this builder + */ + public OidcClientConfigBuilder grantOptions(Map> grantOptions) { + Objects.requireNonNull(grantOptions); + this.grantOptions.putAll(grantOptions); + return this; + } + + /** + * @param absoluteExpiresIn {@link OidcClientConfig#absoluteExpiresIn()} + * @return this builder + */ + public OidcClientConfigBuilder absoluteExpiresIn(boolean absoluteExpiresIn) { + this.absoluteExpiresIn = absoluteExpiresIn; + return this; + } + + /** + * @param accessTokenExpiresIn {@link OidcClientConfig#accessTokenExpiresIn()} + * @return this builder + */ + public OidcClientConfigBuilder accessTokenExpiresIn(Duration accessTokenExpiresIn) { + this.accessTokenExpiresIn = Optional.ofNullable(accessTokenExpiresIn); + return this; + } + + /** + * @param refreshTokenTimeSkew {@link OidcClientConfig#refreshTokenTimeSkew()} + * @return this builder + */ + public OidcClientConfigBuilder refreshTokenTimeSkew(Duration refreshTokenTimeSkew) { + this.refreshTokenTimeSkew = Optional.ofNullable(refreshTokenTimeSkew); + return this; + } + + /** + * Adds scopes to the {@link OidcClientConfig#scopes()}. + * + * @param scopes {@link OidcClientConfig#scopes()} + * @return this builder + */ + public OidcClientConfigBuilder scopes(List scopes) { + Objects.requireNonNull(scopes); + this.scopes.addAll(scopes); + return this; + } + + /** + * Adds scopes to the {@link OidcClientConfig#scopes()}. + * + * @param scopes {@link OidcClientConfig#scopes()} + * @return this builder + */ + public OidcClientConfigBuilder scopes(String... scopes) { + Objects.requireNonNull(scopes); + this.scopes.addAll(Arrays.asList(scopes)); + return this; + } + + /** + * @param clientEnabled {@link OidcClientConfig#clientEnabled()} + * @return this builder + */ + public OidcClientConfigBuilder clientEnabled(boolean clientEnabled) { + this.clientEnabled = clientEnabled; + return this; + } + + /** + * @param id {@link OidcClientConfig#id()} + * @return this builder + */ + public OidcClientConfigBuilder id(String id) { + this.id = Optional.ofNullable(id); + return this; + } + + /** + * @param grant {@link OidcClientConfig#grant()} created either with {@link GrantBuilder} or SmallRye Config + * @return this builder + */ + public OidcClientConfigBuilder grant(Grant grant) { + this.grant = Objects.requireNonNull(grant); + return this; + } + + /** + * @param type {@link Grant#type()} + * @return this builder + */ + public OidcClientConfigBuilder grant(Grant.Type type) { + return grant().type(type).end(); + } + + /** + * Creates {@link OidcClientConfig#grant()} builder. + * + * @return GrantBuilder + */ + public GrantBuilder grant() { + return new GrantBuilder(this); + } + + /** + * @return OidcClientConfig + */ + public OidcClientConfig build() { + if (id.isEmpty()) { + throw new IllegalArgumentException("Client ID cannot be empty"); + } + return new OidcClientConfigImpl(this); + } + + public static final class GrantBuilder { + + private record GrantImpl(Type type, String accessTokenProperty, String refreshTokenProperty, String expiresInProperty, + String refreshExpiresInProperty) implements Grant { + + } + + private final OidcClientConfigBuilder builder; + private Grant.Type type; + private String accessTokenProperty; + private String refreshTokenProperty; + private String expiresInProperty; + private String refreshExpiresInProperty; + + public GrantBuilder() { + this(OidcClientConfig.builder()); + } + + public GrantBuilder(OidcClientConfigBuilder builder) { + this.builder = builder; + this.type = builder.grant.type(); + this.accessTokenProperty = builder.grant.accessTokenProperty(); + this.refreshTokenProperty = builder.grant.refreshTokenProperty(); + this.expiresInProperty = builder.grant.expiresInProperty(); + this.refreshExpiresInProperty = builder.grant.refreshExpiresInProperty(); + } + + /** + * @param type {@link Grant#type()} + * @return this builder + */ + public GrantBuilder type(Grant.Type type) { + this.type = Objects.requireNonNull(type); + return this; + } + + /** + * @param refreshTokenProperty {@link Grant#refreshTokenProperty()} + * @return this builder + */ + public GrantBuilder refreshTokenProperty(String refreshTokenProperty) { + this.refreshTokenProperty = Objects.requireNonNull(refreshTokenProperty); + return this; + } + + /** + * @param expiresInProperty {@link Grant#expiresInProperty()} + * @return this builder + */ + public GrantBuilder expiresInProperty(String expiresInProperty) { + this.expiresInProperty = Objects.requireNonNull(expiresInProperty); + return this; + } + + /** + * @param refreshExpiresInProperty {@link Grant#refreshExpiresInProperty()} + * @return this builder + */ + public GrantBuilder refreshExpiresInProperty(String refreshExpiresInProperty) { + this.refreshExpiresInProperty = Objects.requireNonNull(refreshExpiresInProperty); + return this; + } + + /** + * @param accessTokenProperty {@link Grant#accessTokenProperty()} + * @return this builder + */ + public GrantBuilder accessTokenProperty(String accessTokenProperty) { + this.accessTokenProperty = Objects.requireNonNull(accessTokenProperty); + return this; + } + + public OidcClientConfigBuilder end() { + Objects.requireNonNull(builder); + return builder.grant(build()); + } + + public Grant build() { + return new GrantImpl(type, accessTokenProperty, refreshTokenProperty, expiresInProperty, refreshExpiresInProperty); + } + } +} diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClients.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClients.java index 2fd12835d7e18..8ab370730770c 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClients.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClients.java @@ -2,6 +2,7 @@ import java.io.Closeable; +import io.quarkus.oidc.client.runtime.OidcClientConfig; import io.smallrye.mutiny.Uni; /** @@ -27,7 +28,7 @@ public interface OidcClients extends Closeable { /** * Returns a new {@link OidcClient}. * - * @param id {@link OidcClientConfig} new client configuration + * @param clientConfig {@link OidcClientConfig} new client configuration * @return Uni */ Uni newClient(OidcClientConfig clientConfig); diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/AbstractTokensProducer.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/AbstractTokensProducer.java index 7cc57cc16ee17..93c97f44cf823 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/AbstractTokensProducer.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/AbstractTokensProducer.java @@ -46,7 +46,7 @@ public void init() { earlyTokenAcquisition = oidcClientsConfig.namedClients().get(clientId.get()).earlyTokensAcquisition(); } else { // default OidcClient - earlyTokenAcquisition = oidcClientsConfig.defaultClient().earlyTokensAcquisition(); + earlyTokenAcquisition = OidcClientsConfig.getDefaultClient(oidcClientsConfig).earlyTokensAcquisition(); oidcClient = oidcClients.getClient(); } } else { diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientConfig.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientConfig.java index 06fd501e92e57..8f2ebbbc9d5df 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientConfig.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientConfig.java @@ -5,9 +5,12 @@ import java.util.Map; import java.util.Optional; +import io.quarkus.oidc.client.OidcClientConfigBuilder; import io.quarkus.oidc.common.runtime.OidcConstants; import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig; +import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.runtime.annotations.ConfigDocMapKey; +import io.smallrye.config.SmallRyeConfigBuilder; import io.smallrye.config.WithDefault; public interface OidcClientConfig extends OidcClientCommonConfig { @@ -165,4 +168,58 @@ public String getGrantType() { */ Map headers(); + /** + * Creates {@link OidcClientConfigBuilder} builder populated with documented default values. + * + * @return OidcClientConfigBuilder builder + */ + static OidcClientConfigBuilder builder() { + var clientsConfig = new SmallRyeConfigBuilder() + .addDiscoveredConverters() + .withMapping(OidcClientsConfig.class) + .build() + .getConfigMapping(OidcClientsConfig.class); + return builder(OidcClientsConfig.getDefaultClient(clientsConfig)); + } + + /** + * Creates {@link OidcClientConfigBuilder} builder populated with {@code config} values. + * + * @param config client config; must not be null + * @return OidcClientConfigBuilder + */ + static OidcClientConfigBuilder builder(OidcClientConfig config) { + return new OidcClientConfigBuilder(config); + } + + /** + * Creates {@link OidcClientConfigBuilder} builder populated with documented default values. + * + * @param authServerUrl {@link OidcCommonConfig#authServerUrl()} + * @return OidcClientConfigBuilder builder + */ + static OidcClientConfigBuilder authServerUrl(String authServerUrl) { + return builder().authServerUrl(authServerUrl); + } + + /** + * Creates {@link OidcClientConfigBuilder} builder populated with documented default values. + * + * @param registrationPath {@link OidcCommonConfig#registrationPath()} + * @return OidcClientConfigBuilder builder + */ + static OidcClientConfigBuilder registrationPath(String registrationPath) { + return builder().registrationPath(registrationPath); + } + + /** + * Creates {@link OidcClientConfigBuilder} builder populated with documented default values. + * + * @param tokenPath {@link OidcClientCommonConfig#tokenPath()} + * @return OidcClientConfigBuilder builder + */ + static OidcClientConfigBuilder tokenPath(String tokenPath) { + return builder().tokenPath(tokenPath); + } + } diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientDefaultIdConfigBuilder.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientDefaultIdConfigBuilder.java new file mode 100644 index 0000000000000..4272843ce2505 --- /dev/null +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientDefaultIdConfigBuilder.java @@ -0,0 +1,95 @@ +package io.quarkus.oidc.client.runtime; + +import static io.quarkus.oidc.client.runtime.OidcClientRecorder.DEFAULT_OIDC_CLIENT_ID; + +import java.util.Iterator; +import java.util.OptionalInt; + +import io.quarkus.runtime.configuration.ConfigBuilder; +import io.smallrye.config.ConfigSourceInterceptor; +import io.smallrye.config.ConfigSourceInterceptorContext; +import io.smallrye.config.ConfigSourceInterceptorFactory; +import io.smallrye.config.ConfigValue; +import io.smallrye.config.Priorities; +import io.smallrye.config.SmallRyeConfigBuilder; + +/** + * Sets default {@link OidcClientConfig#id()} to the client's named key. + * For example, the configuration property 'quarkus.oidc-client.<>.id' is set to the '<>' if + * user did not configure any value. + */ +public class OidcClientDefaultIdConfigBuilder implements ConfigBuilder { + + private static final String OIDC_CLIENT_PREFIX = "quarkus.oidc-client."; + private static final String ID_POSTFIX = ".id"; + private static final String DEFAULT_CLIENT_ID_PROPERTY_KEY = OIDC_CLIENT_PREFIX + "id"; + private static final String DOUBLE_QUOTE = "\""; + private static final String WITH_DEFAULTS_ID_KEY = "quarkus.oidc-client.*.id"; + + @Override + public SmallRyeConfigBuilder configBuilder(SmallRyeConfigBuilder builder) { + final ConfigSourceInterceptor configSourceInterceptor = createConfigSourceInterceptor(); + builder.withInterceptorFactories(new ConfigSourceInterceptorFactory() { + + @Override + public ConfigSourceInterceptor getInterceptor(ConfigSourceInterceptorContext configSourceInterceptorContext) { + return configSourceInterceptor; + } + + @Override + public OptionalInt getPriority() { + return OptionalInt.of(Priorities.LIBRARY + 200); + } + }); + return builder; + } + + @Override + public int priority() { + return Integer.MIN_VALUE; + } + + private static boolean isNotSet(ConfigValue configValue) { + return configValue == null || configValue.getValue() == null || configValue.getValue().isEmpty(); + } + + private static ConfigValue createConfigValue(String name, String value) { + return ConfigValue.builder().withName(name).withValue(value).build(); + } + + private static ConfigSourceInterceptor createConfigSourceInterceptor() { + return new ConfigSourceInterceptor() { + @Override + public ConfigValue getValue(ConfigSourceInterceptorContext context, String name) { + var configValue = context.proceed(name); + if (isNotSet(configValue) && name.startsWith(OIDC_CLIENT_PREFIX) && name.endsWith(ID_POSTFIX) + && !WITH_DEFAULTS_ID_KEY.equals(name)) { + if (name.equals(DEFAULT_CLIENT_ID_PROPERTY_KEY)) { + return createConfigValue(name, DEFAULT_OIDC_CLIENT_ID); + } else { + var maybeClientName = name.substring(OIDC_CLIENT_PREFIX.length(), name.length() - ID_POSTFIX.length()); + // this will cause an issue if the client name contained dot + // but the alternative is much more complex because we would have to presume that + // there never will never be other property that starts with the 'quarkus.oidc-client.' + // and ends with '.id' but is not the client id + if (!maybeClientName.contains(".")) { + // this is additional named client, now we know that OIDC client extension validates + // the 'id' always equals named key, so we can preset this for users + if (maybeClientName.startsWith(DOUBLE_QUOTE) && maybeClientName.endsWith(DOUBLE_QUOTE)) { + var clientNameWithoutQuotes = maybeClientName.substring(1, maybeClientName.length() - 1); + return createConfigValue(name, clientNameWithoutQuotes); + } + return createConfigValue(name, maybeClientName); + } + } + } + return configValue; + } + + @Override + public Iterator iterateNames(ConfigSourceInterceptorContext context) { + return context.iterateNames(); + } + }; + } +} diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java index fab6aacf510ae..3b820603f7d3d 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java @@ -15,7 +15,6 @@ import org.jboss.logging.Logger; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig; import io.quarkus.oidc.client.OidcClientException; import io.quarkus.oidc.client.Tokens; import io.quarkus.oidc.common.OidcEndpoint; @@ -23,9 +22,9 @@ import io.quarkus.oidc.common.OidcRequestFilter; import io.quarkus.oidc.common.OidcRequestFilter.OidcRequestContext; import io.quarkus.oidc.common.OidcResponseFilter; -import io.quarkus.oidc.common.runtime.OidcClientCommonConfig.Credentials.Jwt.Source; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt.Source; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.groups.UniOnItem; import io.vertx.core.http.HttpHeaders; @@ -72,7 +71,7 @@ public OidcClientImpl(WebClient client, String tokenRequestUri, String tokenRevo this.requestFilters = requestFilters; this.responseFilters = responseFilters; this.clientSecretBasicAuthScheme = OidcCommonUtils.initClientSecretBasicAuth(oidcClientConfig); - this.jwtBearerAuthentication = oidcClientConfig.credentials.jwt.source == Source.BEARER; + this.jwtBearerAuthentication = oidcClientConfig.credentials().jwt().source() == Source.BEARER; this.clientJwtKey = jwtBearerAuthentication ? null : OidcCommonUtils.initClientJwtKey(oidcClientConfig, false); } @@ -124,7 +123,7 @@ private OidcRequestContextProperties getRequestProps(String grantType) { return null; } Map props = new HashMap<>(); - props.put(CLIENT_ID_ATTRIBUTE, oidcConfig.getId().orElse(DEFAULT_OIDC_CLIENT_ID)); + props.put(CLIENT_ID_ATTRIBUTE, oidcConfig.id().orElse(DEFAULT_OIDC_CLIENT_ID)); if (grantType != null) { props.put(OidcConstants.GRANT_TYPE, grantType); } @@ -169,8 +168,8 @@ private UniOnItem> postRequest( MultiMap body = formBody; request.putHeader(HttpHeaders.CONTENT_TYPE.toString(), HttpHeaders.APPLICATION_X_WWW_FORM_URLENCODED.toString()); - if (oidcConfig.headers != null) { - for (Map.Entry headerEntry : oidcConfig.headers.entrySet()) { + if (oidcConfig.headers() != null) { + for (Map.Entry headerEntry : oidcConfig.headers().entrySet()) { request.putHeader(headerEntry.getKey(), headerEntry.getValue()); } } @@ -181,7 +180,7 @@ private UniOnItem> postRequest( if (!additionalGrantParameters.containsKey(OidcConstants.CLIENT_ASSERTION)) { String errorMessage = String.format( "%s OidcClient can not complete the %s grant request because a JWT bearer client_assertion is missing", - oidcConfig.getId().get(), (refresh ? OidcConstants.REFRESH_TOKEN_GRANT : grantType)); + oidcConfig.id().get(), (refresh ? OidcConstants.REFRESH_TOKEN_GRANT : grantType)); LOG.error(errorMessage); throw new OidcClientException(errorMessage); } @@ -191,15 +190,15 @@ private UniOnItem> postRequest( body = !refresh ? copyMultiMap(body) : body; String jwt = OidcCommonUtils.signJwtWithKey(oidcConfig, tokenRequestUri, clientJwtKey); - if (OidcCommonUtils.isClientSecretPostJwtAuthRequired(oidcConfig.credentials)) { - body.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); + if (OidcCommonUtils.isClientSecretPostJwtAuthRequired(oidcConfig.credentials())) { + body.add(OidcConstants.CLIENT_ID, oidcConfig.clientId().get()); body.add(OidcConstants.CLIENT_SECRET, jwt); - } else if (OidcCommonUtils.isJwtAssertion(oidcConfig.credentials)) { + } else if (OidcCommonUtils.isJwtAssertion(oidcConfig.credentials())) { if (!OidcConstants.JWT_BEARER_GRANT_TYPE.equals(body.get(OidcConstants.GRANT_TYPE))) { String errorMessage = String.format( "%s OidcClient wants to use JWT bearer grant assertion but has a wrong grant type %s configured." + " You must set 'quarkus.oidc-client.grant.type' property to 'jwt'.", - oidcConfig.getId().get(), body.get(OidcConstants.GRANT_TYPE)); + oidcConfig.id().get(), body.get(OidcConstants.GRANT_TYPE)); LOG.error(errorMessage); throw new OidcClientException(errorMessage); } @@ -208,13 +207,13 @@ private UniOnItem> postRequest( body.add(OidcConstants.CLIENT_ASSERTION_TYPE, OidcConstants.JWT_BEARER_CLIENT_ASSERTION_TYPE); body.add(OidcConstants.CLIENT_ASSERTION, jwt); } - } else if (OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials)) { + } else if (OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials())) { body = !refresh ? copyMultiMap(body) : body; - body.set(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); - body.set(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials)); + body.set(OidcConstants.CLIENT_ID, oidcConfig.clientId().get()); + body.set(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials())); } else { body = !refresh ? copyMultiMap(body) : body; - body = copyMultiMap(body).set(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); + body = copyMultiMap(body).set(OidcConstants.CLIENT_ID, oidcConfig.clientId().get()); } if (!additionalGrantParameters.isEmpty()) { body = copyMultiMap(body); @@ -227,7 +226,7 @@ private UniOnItem> postRequest( Uni> response = filterHttpRequest(requestProps, endpointType, request, buffer).sendBuffer(buffer) .onFailure(ConnectException.class) .retry() - .atMost(oidcConfig.connectionRetryCount) + .atMost(oidcConfig.connectionRetryCount()) .onFailure().transform(t -> { LOG.warn("OIDC Server is not available:", t.getCause() != null ? t.getCause() : t); // don't wrap it to avoid information leak @@ -240,23 +239,23 @@ private Tokens emitGrantTokens(OidcRequestContextProperties requestProps, HttpRe Buffer buffer = resp.body(); OidcCommonUtils.filterHttpResponse(requestProps, resp, buffer, responseFilters, OidcEndpoint.Type.TOKEN); if (resp.statusCode() == 200) { - LOG.debugf("%s OidcClient has %s the tokens", oidcConfig.getId().get(), (refresh ? "refreshed" : "acquired")); + LOG.debugf("%s OidcClient has %s the tokens", oidcConfig.id().get(), (refresh ? "refreshed" : "acquired")); JsonObject json = buffer.toJsonObject(); // access token - final String accessToken = json.getString(oidcConfig.grant.accessTokenProperty); + final String accessToken = json.getString(oidcConfig.grant().accessTokenProperty()); final Long accessTokenExpiresAt = getAccessTokenExpiresAtValue(accessToken, - json.getValue(oidcConfig.grant.expiresInProperty)); + json.getValue(oidcConfig.grant().expiresInProperty())); - final String refreshToken = json.getString(oidcConfig.grant.refreshTokenProperty); + final String refreshToken = json.getString(oidcConfig.grant().refreshTokenProperty()); final Long refreshTokenExpiresAt = getExpiresAtValue(refreshToken, - json.getValue(oidcConfig.grant.refreshExpiresInProperty)); + json.getValue(oidcConfig.grant().refreshExpiresInProperty())); - return new Tokens(accessToken, accessTokenExpiresAt, oidcConfig.refreshTokenTimeSkew.orElse(null), refreshToken, - refreshTokenExpiresAt, json, oidcConfig.clientId.orElse(DEFAULT_OIDC_CLIENT_ID)); + return new Tokens(accessToken, accessTokenExpiresAt, oidcConfig.refreshTokenTimeSkew().orElse(null), refreshToken, + refreshTokenExpiresAt, json, oidcConfig.clientId().orElse(DEFAULT_OIDC_CLIENT_ID)); } else { String errorMessage = buffer.toString(); LOG.debugf("%s OidcClient has failed to complete the %s grant request: status: %d, error message: %s", - oidcConfig.getId().get(), (refresh ? OidcConstants.REFRESH_TOKEN_GRANT : grantType), resp.statusCode(), + oidcConfig.id().get(), (refresh ? OidcConstants.REFRESH_TOKEN_GRANT : grantType), resp.statusCode(), errorMessage); throw new OidcClientException(errorMessage); } @@ -264,9 +263,9 @@ private Tokens emitGrantTokens(OidcRequestContextProperties requestProps, HttpRe private Long getAccessTokenExpiresAtValue(String token, Object expiresInValue) { Long expiresAt = getExpiresAtValue(token, expiresInValue); - if (expiresAt == null && oidcConfig.accessTokenExpiresIn.isPresent()) { + if (expiresAt == null && oidcConfig.accessTokenExpiresIn().isPresent()) { final long now = System.currentTimeMillis() / 1000; - expiresAt = now + oidcConfig.accessTokenExpiresIn.get().toSeconds(); + expiresAt = now + oidcConfig.accessTokenExpiresIn().get().toSeconds(); } return expiresAt; } @@ -275,7 +274,7 @@ private Long getExpiresAtValue(String token, Object expiresInValue) { if (expiresInValue != null) { long tokenExpiresIn = expiresInValue instanceof Number ? ((Number) expiresInValue).longValue() : Long.parseLong(expiresInValue.toString()); - return oidcConfig.absoluteExpiresIn ? tokenExpiresIn + return oidcConfig.absoluteExpiresIn() ? tokenExpiresIn : Instant.now().getEpochSecond() + tokenExpiresIn; } else { return token != null ? getExpiresJwtClaim(token) : null; @@ -326,7 +325,7 @@ public void close() throws IOException { private void checkClosed() { if (closed) { - throw new IllegalStateException("OidcClient " + oidcConfig.getId().get() + " is closed"); + throw new IllegalStateException("OidcClient " + oidcConfig.id().get() + " is closed"); } } diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java index 604c7b159622b..5b9c1e6f64faf 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java @@ -16,11 +16,10 @@ import io.quarkus.arc.Arc; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig; -import io.quarkus.oidc.client.OidcClientConfig.Grant; import io.quarkus.oidc.client.OidcClientException; import io.quarkus.oidc.client.OidcClients; import io.quarkus.oidc.client.Tokens; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant; import io.quarkus.oidc.common.OidcEndpoint; import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; @@ -42,30 +41,32 @@ public class OidcClientRecorder { private static final Logger LOG = Logger.getLogger(OidcClientRecorder.class); private static final String CLIENT_ID_ATTRIBUTE = "client-id"; - private static final String DEFAULT_OIDC_CLIENT_ID = "Default"; + static final String DEFAULT_OIDC_CLIENT_ID = "Default"; private static OidcClients setup(OidcClientsConfig oidcClientsConfig, Supplier vertx, Supplier registrySupplier) { var tlsSupport = OidcTlsSupport.of(registrySupplier); - var defaultClientConfig = new OidcClientConfig(oidcClientsConfig.defaultClient()); - String defaultClientId = defaultClientConfig.getId().orElse(DEFAULT_OIDC_CLIENT_ID); + var defaultClientConfig = OidcClientsConfig.getDefaultClient(oidcClientsConfig); + String defaultClientId = defaultClientConfig.id().get(); OidcClient defaultClient = createOidcClient(defaultClientConfig, defaultClientId, vertx, tlsSupport); Map staticOidcClients = new HashMap<>(); for (var config : oidcClientsConfig.namedClients().entrySet()) { - var namedOidcClientConfig = new OidcClientConfig(config.getValue()); - OidcCommonUtils.verifyConfigurationId(defaultClientId, config.getKey(), namedOidcClientConfig.getId()); - staticOidcClients.put(config.getKey(), - createOidcClient(namedOidcClientConfig, config.getKey(), vertx, tlsSupport)); + final String namedKey = config.getKey(); + if (!OidcClientsConfig.DEFAULT_CLIENT_KEY.equals(namedKey)) { + var namedOidcClientConfig = config.getValue(); + OidcCommonUtils.verifyConfigurationId(defaultClientId, namedKey, namedOidcClientConfig.id()); + staticOidcClients.put(namedKey, createOidcClient(namedOidcClientConfig, namedKey, vertx, tlsSupport)); + } } return new OidcClientsImpl(defaultClient, staticOidcClients, new Function>() { @Override public Uni apply(OidcClientConfig config) { - return createOidcClientUni(config, config.getId().get(), vertx, OidcTlsSupport.of(registrySupplier)); + return createOidcClientUni(config, config.id().get(), vertx, OidcTlsSupport.of(registrySupplier)); } }); } @@ -104,22 +105,25 @@ public OidcClients get() { protected static OidcClient createOidcClient(OidcClientConfig oidcConfig, String oidcClientId, Supplier vertx, OidcTlsSupport tlsSupport) { return createOidcClientUni(oidcConfig, oidcClientId, vertx, tlsSupport).await() - .atMost(oidcConfig.connectionTimeout); + .atMost(oidcConfig.connectionTimeout()); } protected static Uni createOidcClientUni(OidcClientConfig oidcConfig, String oidcClientId, Supplier vertx, OidcTlsSupport tlsSupport) { - if (!oidcConfig.isClientEnabled()) { + if (!oidcConfig.clientEnabled()) { String message = String.format("'%s' client configuration is disabled", oidcClientId); LOG.debug(message); return Uni.createFrom().item(new DisabledOidcClient(message)); } - if (!oidcConfig.getId().isPresent()) { - oidcConfig.setId(oidcClientId); + if (oidcConfig.id().isEmpty()) { + // if user did not set the client id + // we do set 'id' to the named client key + // e.g. quarkus.oidc-client.<>.id=<> + return Uni.createFrom().failure(new IllegalStateException("OIDC Client ID must be set")); } try { - if (oidcConfig.authServerUrl.isEmpty() && !OidcCommonUtils.isAbsoluteUrl(oidcConfig.tokenPath)) { + if (oidcConfig.authServerUrl().isEmpty() && !OidcCommonUtils.isAbsoluteUrl(oidcConfig.tokenPath())) { throw new ConfigurationException( "Either 'quarkus.oidc-client.auth-server-url' or absolute 'quarkus.oidc-client.token-path' URL must be set"); } @@ -132,8 +136,8 @@ protected static Uni createOidcClientUni(OidcClientConfig oidcConfig } WebClientOptions options = new WebClientOptions(); - options.setFollowRedirects(oidcConfig.followRedirects); - OidcCommonUtils.setHttpClientOptions(oidcConfig, options, tlsSupport.forConfig(oidcConfig.tls)); + options.setFollowRedirects(oidcConfig.followRedirects()); + OidcCommonUtils.setHttpClientOptions(oidcConfig, options, tlsSupport.forConfig(oidcConfig.tls())); var mutinyVertx = new io.vertx.mutiny.core.Vertx(vertx.get()); WebClient client = WebClient.create(mutinyVertx, options); @@ -141,17 +145,17 @@ protected static Uni createOidcClientUni(OidcClientConfig oidcConfig Map> oidcRequestFilters = OidcCommonUtils.getOidcRequestFilters(); Map> oidcResponseFilters = OidcCommonUtils.getOidcResponseFilters(); Uni tokenUrisUni = null; - if (OidcCommonUtils.isAbsoluteUrl(oidcConfig.tokenPath)) { + if (OidcCommonUtils.isAbsoluteUrl(oidcConfig.tokenPath())) { tokenUrisUni = Uni.createFrom().item( - new OidcConfigurationMetadata(oidcConfig.tokenPath.get(), - OidcCommonUtils.isAbsoluteUrl(oidcConfig.revokePath) ? oidcConfig.revokePath.get() : null)); + new OidcConfigurationMetadata(oidcConfig.tokenPath().get(), + OidcCommonUtils.isAbsoluteUrl(oidcConfig.revokePath()) ? oidcConfig.revokePath().get() : null)); } else { String authServerUriString = OidcCommonUtils.getAuthServerUrl(oidcConfig); - if (!oidcConfig.discoveryEnabled.orElse(true)) { + if (!oidcConfig.discoveryEnabled().orElse(true)) { tokenUrisUni = Uni.createFrom() .item(new OidcConfigurationMetadata( - OidcCommonUtils.getOidcEndpointUrl(authServerUriString, oidcConfig.tokenPath), - OidcCommonUtils.getOidcEndpointUrl(authServerUriString, oidcConfig.revokePath))); + OidcCommonUtils.getOidcEndpointUrl(authServerUriString, oidcConfig.tokenPath()), + OidcCommonUtils.getOidcEndpointUrl(authServerUriString, oidcConfig.revokePath()))); } else { tokenUrisUni = discoverTokenUris(client, oidcRequestFilters, oidcResponseFilters, authServerUriString.toString(), oidcConfig, @@ -171,19 +175,19 @@ public OidcClient apply(OidcConfigurationMetadata metadata, Throwable t) { throw new ConfigurationException( "OpenId Connect Provider token endpoint URL is not configured and can not be discovered"); } - String grantType = oidcConfig.grant.getType().getGrantType(); + String grantType = oidcConfig.grant().type().getGrantType(); MultiMap tokenGrantParams = null; - if (oidcConfig.grant.getType() != Grant.Type.REFRESH) { + if (oidcConfig.grant().type() != Grant.Type.REFRESH) { tokenGrantParams = new MultiMap(io.vertx.core.MultiMap.caseInsensitiveMultiMap()); setGrantClientParams(oidcConfig, tokenGrantParams, grantType); - if (oidcConfig.getGrantOptions() != null) { - Map grantOptions = oidcConfig.getGrantOptions() - .get(oidcConfig.grant.getType().name().toLowerCase()); + if (oidcConfig.grantOptions() != null) { + Map grantOptions = oidcConfig.grantOptions() + .get(oidcConfig.grant().type().name().toLowerCase()); if (grantOptions != null) { - if (oidcConfig.grant.getType() == Grant.Type.PASSWORD) { + if (oidcConfig.grant().type() == Grant.Type.PASSWORD) { // Without this block `password` will be listed first, before `username` // which is not a technical problem but might affect Wiremock tests or the endpoints // which expect a specific order. @@ -225,13 +229,13 @@ public OidcClient apply(OidcConfigurationMetadata metadata, Throwable t) { } private static String getEndpointUrl(OidcClientConfig oidcConfig) { - return oidcConfig.authServerUrl.isPresent() ? oidcConfig.authServerUrl.get() : oidcConfig.tokenPath.get(); + return oidcConfig.authServerUrl().isPresent() ? oidcConfig.authServerUrl().get() : oidcConfig.tokenPath().get(); } private static void setGrantClientParams(OidcClientConfig oidcConfig, MultiMap grantParams, String grantType) { grantParams.add(OidcConstants.GRANT_TYPE, grantType); - if (oidcConfig.getScopes().isPresent()) { - grantParams.add(OidcConstants.TOKEN_SCOPE, oidcConfig.getScopes().get().stream().collect(Collectors.joining(" "))); + if (oidcConfig.scopes().isPresent()) { + grantParams.add(OidcConstants.TOKEN_SCOPE, oidcConfig.scopes().get().stream().collect(Collectors.joining(" "))); } } @@ -241,9 +245,9 @@ private static Uni discoverTokenUris(WebClient client String authServerUrl, OidcClientConfig oidcConfig, io.vertx.mutiny.core.Vertx vertx) { final long connectionDelayInMillisecs = OidcCommonUtils.getConnectionDelayInMillis(oidcConfig); OidcRequestContextProperties contextProps = new OidcRequestContextProperties( - Map.of(CLIENT_ID_ATTRIBUTE, oidcConfig.getId().orElse(DEFAULT_OIDC_CLIENT_ID))); + Map.of(CLIENT_ID_ATTRIBUTE, oidcConfig.id().orElse(DEFAULT_OIDC_CLIENT_ID))); return OidcCommonUtils.discoverMetadata(client, oidcRequestFilters, contextProps, oidcResponseFilters, - authServerUrl, connectionDelayInMillisecs, vertx, oidcConfig.useBlockingDnsLookup) + authServerUrl, connectionDelayInMillisecs, vertx, oidcConfig.useBlockingDnsLookup()) .onItem().transform(json -> new OidcConfigurationMetadata(json.getString("token_endpoint"), json.getString("revocation_endpoint"))); } diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsConfig.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsConfig.java index 4c76cb46e88d0..ee776eea980e0 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsConfig.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsConfig.java @@ -3,27 +3,29 @@ import java.util.Map; import io.quarkus.runtime.annotations.ConfigDocMapKey; -import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefaults; import io.smallrye.config.WithParentName; +import io.smallrye.config.WithUnnamedKey; @ConfigMapping(prefix = "quarkus.oidc-client") @ConfigRoot(phase = ConfigPhase.RUN_TIME) public interface OidcClientsConfig { - /** - * The default client. - */ - @WithParentName - OidcClientConfig defaultClient(); + String DEFAULT_CLIENT_KEY = ""; /** * Additional named clients. */ - @ConfigDocSection @ConfigDocMapKey("id") @WithParentName + @WithUnnamedKey(DEFAULT_CLIENT_KEY) + @WithDefaults Map namedClients(); + + static OidcClientConfig getDefaultClient(OidcClientsConfig config) { + return config.namedClients().get(DEFAULT_CLIENT_KEY); + } } diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsImpl.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsImpl.java index 5116c03b0d30e..02c4da69d7d68 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsImpl.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsImpl.java @@ -6,7 +6,6 @@ import java.util.function.Function; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig; import io.quarkus.oidc.client.OidcClientException; import io.quarkus.oidc.client.OidcClients; import io.smallrye.mutiny.Uni; @@ -46,7 +45,7 @@ public void close() throws IOException { @Override public Uni newClient(OidcClientConfig clientConfig) { - if (!clientConfig.getId().isPresent()) { + if (clientConfig.id().isEmpty()) { throw new OidcClientException("'id' property must be set"); } return dynamicOidcClients.apply(clientConfig); diff --git a/extensions/oidc-client/runtime/src/test/java/io/quarkus/oidc/client/OidcClientConfigBuilderTest.java b/extensions/oidc-client/runtime/src/test/java/io/quarkus/oidc/client/OidcClientConfigBuilderTest.java new file mode 100644 index 0000000000000..e32ffb2873c44 --- /dev/null +++ b/extensions/oidc-client/runtime/src/test/java/io/quarkus/oidc/client/OidcClientConfigBuilderTest.java @@ -0,0 +1,730 @@ +package io.quarkus.oidc.client; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.quarkus.oidc.client.runtime.OidcClientConfig; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant.Type; +import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt.Source; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret.Method; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfigBuilder.CredentialsBuilder; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfigBuilder.JwtBuilder; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfigBuilder.SecretBuilder; + +public class OidcClientConfigBuilderTest { + + @Test + public void testDefaultValues() { + var config = OidcClientConfig.builder().id("default-test").build(); + + // OidcClientConfig methods + assertEquals("default-test", config.id().orElse(null)); + assertTrue(config.clientEnabled()); + assertTrue(config.scopes().isEmpty()); + assertTrue(config.refreshTokenTimeSkew().isEmpty()); + assertTrue(config.accessTokenExpiresIn().isEmpty()); + assertFalse(config.absoluteExpiresIn()); + assertTrue(config.grantOptions().isEmpty()); + assertTrue(config.earlyTokensAcquisition()); + assertTrue(config.headers().isEmpty()); + var grant = config.grant(); + assertNotNull(grant); + assertEquals(Type.CLIENT, grant.type()); + assertEquals(OidcConstants.ACCESS_TOKEN_VALUE, grant.accessTokenProperty()); + assertEquals(OidcConstants.REFRESH_TOKEN_VALUE, grant.refreshTokenProperty()); + assertEquals(OidcConstants.EXPIRES_IN, grant.expiresInProperty()); + assertEquals(OidcConstants.REFRESH_EXPIRES_IN, grant.refreshExpiresInProperty()); + + // OidcClientCommonConfig methods + assertTrue(config.tokenPath().isEmpty()); + assertTrue(config.revokePath().isEmpty()); + assertTrue(config.clientId().isEmpty()); + assertTrue(config.clientName().isEmpty()); + var credentials = config.credentials(); + assertNotNull(credentials); + assertTrue(credentials.secret().isEmpty()); + var clientSecret = credentials.clientSecret(); + assertNotNull(clientSecret); + assertTrue(clientSecret.value().isEmpty()); + assertTrue(clientSecret.method().isEmpty()); + var provider = clientSecret.provider(); + assertNotNull(provider); + assertTrue(provider.key().isEmpty()); + assertTrue(provider.keyringName().isEmpty()); + assertTrue(provider.name().isEmpty()); + var jwt = credentials.jwt(); + assertNotNull(jwt); + assertEquals(Source.CLIENT, jwt.source()); + assertTrue(jwt.secret().isEmpty()); + provider = jwt.secretProvider(); + assertNotNull(provider); + assertTrue(provider.key().isEmpty()); + assertTrue(provider.keyringName().isEmpty()); + assertTrue(provider.name().isEmpty()); + assertTrue(jwt.key().isEmpty()); + assertTrue(jwt.keyFile().isEmpty()); + assertTrue(jwt.keyStoreFile().isEmpty()); + assertTrue(jwt.keyStorePassword().isEmpty()); + assertTrue(jwt.keyId().isEmpty()); + assertTrue(jwt.keyPassword().isEmpty()); + assertTrue(jwt.audience().isEmpty()); + assertTrue(jwt.tokenKeyId().isEmpty()); + assertTrue(jwt.issuer().isEmpty()); + assertTrue(jwt.subject().isEmpty()); + assertTrue(jwt.claims().isEmpty()); + assertTrue(jwt.signatureAlgorithm().isEmpty()); + assertEquals(10, jwt.lifespan()); + assertFalse(jwt.assertion()); + + // OidcCommonConfig methods + assertTrue(config.authServerUrl().isEmpty()); + assertTrue(config.discoveryEnabled().isEmpty()); + assertTrue(config.registrationPath().isEmpty()); + assertTrue(config.connectionDelay().isEmpty()); + assertEquals(3, config.connectionRetryCount()); + assertEquals(10, config.connectionTimeout().getSeconds()); + assertFalse(config.useBlockingDnsLookup()); + assertTrue(config.maxPoolSize().isEmpty()); + assertTrue(config.followRedirects()); + assertNotNull(config.proxy()); + assertTrue(config.proxy().host().isEmpty()); + assertEquals(80, config.proxy().port()); + assertTrue(config.proxy().username().isEmpty()); + assertTrue(config.proxy().password().isEmpty()); + assertNotNull(config.tls()); + assertTrue(config.tls().tlsConfigurationName().isEmpty()); + assertTrue(config.tls().verification().isEmpty()); + assertTrue(config.tls().keyStoreFile().isEmpty()); + assertTrue(config.tls().keyStoreFileType().isEmpty()); + assertTrue(config.tls().keyStoreProvider().isEmpty()); + assertTrue(config.tls().keyStorePassword().isEmpty()); + assertTrue(config.tls().keyStoreKeyAlias().isEmpty()); + assertTrue(config.tls().keyStoreKeyPassword().isEmpty()); + assertTrue(config.tls().trustStoreFile().isEmpty()); + assertTrue(config.tls().trustStorePassword().isEmpty()); + assertTrue(config.tls().trustStoreCertAlias().isEmpty()); + assertTrue(config.tls().trustStoreFileType().isEmpty()); + assertTrue(config.tls().trustStoreProvider().isEmpty()); + } + + @Test + public void testSetEveryProperty() { + var config = OidcClientConfig.builder() + // OidcClientConfig methods + .id("set-every-property-test") + .clientEnabled(false) + .scopes("one", "two") + .scopes(List.of("three", "four")) + .refreshTokenTimeSkew(Duration.ofSeconds(987)) + .accessTokenExpiresIn(Duration.ofSeconds(789)) + .absoluteExpiresIn(true) + .grant() + .type(Type.CODE) + .accessTokenProperty("access_token_test") + .refreshTokenProperty("refresh_token_test") + .expiresInProperty("expires_in_test") + .refreshExpiresInProperty("refresh_expires_in_test") + .end() + .grantOptions("one", "two", "three") + .grantOptions("four", Map.of("five", "six")) + .grantOptions(Map.of("seven", Map.of("eight", "nine"))) + .earlyTokensAcquisition(false) + .headers("one", "two") + .headers(Map.of("three", "four")) + // OidcClientCommonConfig methods + .tokenPath("token-path-yep") + .revokePath("revoke-path-yep") + .clientId("client-id-yep") + .clientName("client-name-yep") + .credentials() + .secret("secret-yep") + .clientSecret() + .method(Method.QUERY) + .value("value-yep") + .provider("key-yep", "name-yep", "keyring-name-yep") + .end() + .jwt() + .source(Source.BEARER) + .secretProvider() + .keyringName("jwt-keyring-name-yep") + .key("jwt-key-yep") + .name("jwt-name-yep") + .end() + .secret("jwt-secret-yep") + .key("jwt-key-yep") + .keyFile("jwt-key-file-yep") + .keyStoreFile("jwt-key-store-file-yep") + .keyStorePassword("jwt-key-store-password-yep") + .keyId("jwt-key-id-yep") + .keyPassword("jwt-key-pwd-yep") + .audience("jwt-audience-yep") + .tokenKeyId("jwt-token-key-id-yep") + .issuer("jwt-issuer") + .subject("jwt-subject") + .claim("claim-one-name", "claim-one-value") + .claims(Map.of("claim-two-name", "claim-two-value")) + .signatureAlgorithm("ES512") + .lifespan(852) + .assertion(true) + .endCredentials() + // OidcCommonConfig methods + .authServerUrl("we") + .discoveryEnabled(false) + .registrationPath("don't") + .connectionDelay(Duration.ofSeconds(656)) + .connectionRetryCount(565) + .connectionTimeout(Duration.ofSeconds(673)) + .useBlockingDnsLookup(true) + .maxPoolSize(376) + .followRedirects(false) + .proxy("need", 55, "no", "education") + .tlsConfigurationName("Teacher!") + .build(); + + // OidcClientConfig methods + assertEquals("set-every-property-test", config.id().orElse(null)); + assertFalse(config.clientEnabled()); + assertTrue(config.scopes().isPresent()); + List scopes = config.scopes().get(); + assertEquals(4, scopes.size()); + assertTrue(scopes.contains("one")); + assertTrue(scopes.contains("two")); + assertTrue(scopes.contains("three")); + assertTrue(scopes.contains("four")); + assertEquals(987, config.refreshTokenTimeSkew().map(Duration::getSeconds).orElse(-1L)); + assertEquals(789, config.accessTokenExpiresIn().map(Duration::getSeconds).orElse(-1L)); + assertTrue(config.absoluteExpiresIn()); + var grant = config.grant(); + assertNotNull(grant); + assertEquals(Type.CODE, grant.type()); + assertEquals("access_token_test", grant.accessTokenProperty()); + assertEquals("refresh_token_test", grant.refreshTokenProperty()); + assertEquals("expires_in_test", grant.expiresInProperty()); + assertEquals("refresh_expires_in_test", grant.refreshExpiresInProperty()); + var grantOptions = config.grantOptions(); + assertNotNull(grantOptions); + assertEquals(3, grantOptions.size()); + assertTrue(grantOptions.containsKey("one")); + assertEquals("three", grantOptions.get("one").get("two")); + assertTrue(grantOptions.containsKey("four")); + assertEquals("six", grantOptions.get("four").get("five")); + assertTrue(grantOptions.containsKey("seven")); + assertEquals("nine", grantOptions.get("seven").get("eight")); + assertFalse(config.earlyTokensAcquisition()); + var headers = config.headers(); + assertNotNull(headers); + assertEquals(2, headers.size()); + assertTrue(headers.containsKey("one")); + assertEquals("two", headers.get("one")); + assertTrue(headers.containsKey("three")); + assertEquals("four", headers.get("three")); + + // OidcClientCommonConfig methods + assertEquals("token-path-yep", config.tokenPath().orElse(null)); + assertEquals("revoke-path-yep", config.revokePath().orElse(null)); + assertEquals("client-id-yep", config.clientId().orElse(null)); + assertEquals("client-name-yep", config.clientName().orElse(null)); + var credentials = config.credentials(); + assertNotNull(credentials); + assertEquals("secret-yep", credentials.secret().orElse(null)); + var clientSecret = credentials.clientSecret(); + assertNotNull(clientSecret); + assertEquals(Method.QUERY, clientSecret.method().orElse(null)); + assertEquals("value-yep", clientSecret.value().orElse(null)); + var provider = clientSecret.provider(); + assertNotNull(provider); + assertEquals("key-yep", provider.key().orElse(null)); + assertEquals("name-yep", provider.name().orElse(null)); + assertEquals("keyring-name-yep", provider.keyringName().orElse(null)); + var jwt = credentials.jwt(); + assertNotNull(jwt); + assertEquals(Source.BEARER, jwt.source()); + assertEquals("jwt-secret-yep", jwt.secret().orElse(null)); + provider = jwt.secretProvider(); + assertNotNull(provider); + assertEquals("jwt-keyring-name-yep", provider.keyringName().orElse(null)); + assertEquals("jwt-name-yep", provider.name().orElse(null)); + assertEquals("jwt-key-yep", provider.key().orElse(null)); + assertEquals("jwt-key-yep", jwt.key().orElse(null)); + assertEquals("jwt-key-file-yep", jwt.keyFile().orElse(null)); + assertEquals("jwt-key-store-file-yep", jwt.keyStoreFile().orElse(null)); + assertEquals("jwt-key-store-password-yep", jwt.keyStorePassword().orElse(null)); + assertEquals("jwt-key-id-yep", jwt.keyId().orElse(null)); + assertEquals("jwt-key-pwd-yep", jwt.keyPassword().orElse(null)); + assertEquals("jwt-audience-yep", jwt.audience().orElse(null)); + assertEquals("jwt-token-key-id-yep", jwt.tokenKeyId().orElse(null)); + assertEquals("jwt-issuer", jwt.issuer().orElse(null)); + assertEquals("jwt-subject", jwt.subject().orElse(null)); + var claims = jwt.claims(); + assertNotNull(claims); + assertEquals(2, claims.size()); + assertTrue(claims.containsKey("claim-one-name")); + assertEquals("claim-one-value", claims.get("claim-one-name")); + assertTrue(claims.containsKey("claim-two-name")); + assertEquals("claim-two-value", claims.get("claim-two-name")); + assertEquals("ES512", jwt.signatureAlgorithm().orElse(null)); + assertEquals(852, jwt.lifespan()); + assertTrue(jwt.assertion()); + + // OidcCommonConfig methods + assertEquals("we", config.authServerUrl().orElse(null)); + assertFalse(config.discoveryEnabled().orElse(false)); + assertEquals("don't", config.registrationPath().orElse(null)); + assertEquals(656, config.connectionDelay().map(Duration::getSeconds).orElse(null)); + assertEquals(565, config.connectionRetryCount()); + assertEquals(673, config.connectionTimeout().getSeconds()); + assertTrue(config.useBlockingDnsLookup()); + assertEquals(376, config.maxPoolSize().orElse(0)); + assertFalse(config.followRedirects()); + assertNotNull(config.proxy()); + assertEquals("need", config.proxy().host().orElse(null)); + assertEquals(55, config.proxy().port()); + assertEquals("no", config.proxy().username().orElse(null)); + assertEquals("education", config.proxy().password().orElse(null)); + assertNotNull(config.tls()); + assertEquals("Teacher!", config.tls().tlsConfigurationName().orElse(null)); + assertTrue(config.tls().verification().isEmpty()); + assertTrue(config.tls().keyStoreFile().isEmpty()); + assertTrue(config.tls().keyStoreFileType().isEmpty()); + assertTrue(config.tls().keyStoreProvider().isEmpty()); + assertTrue(config.tls().keyStorePassword().isEmpty()); + assertTrue(config.tls().keyStoreKeyAlias().isEmpty()); + assertTrue(config.tls().keyStoreKeyPassword().isEmpty()); + assertTrue(config.tls().trustStoreFile().isEmpty()); + assertTrue(config.tls().trustStorePassword().isEmpty()); + assertTrue(config.tls().trustStoreCertAlias().isEmpty()); + assertTrue(config.tls().trustStoreFileType().isEmpty()); + assertTrue(config.tls().trustStoreProvider().isEmpty()); + } + + @Test + public void testCopyProxyProperties() { + var previousConfig = OidcClientConfig.builder() + .id("copy-proxy-properties-test") + .proxy("need", 55, "no", "education") + .build(); + var newConfig = OidcClientConfig.builder(previousConfig) + .proxy("fast-car", 22) + .build(); + + assertNotNull(previousConfig.proxy()); + assertEquals("copy-proxy-properties-test", newConfig.id().orElse(null)); + assertEquals("fast-car", newConfig.proxy().host().orElse(null)); + assertEquals(22, newConfig.proxy().port()); + assertEquals("no", newConfig.proxy().username().orElse(null)); + assertEquals("education", newConfig.proxy().password().orElse(null)); + } + + @Test + public void testCopyOidcClientConfigProperties() { + var existingConfig = OidcClientConfig.builder() + // OidcClientConfig methods + .id("test-copy-client-props") + .clientEnabled(false) + .scopes("one", "two") + .scopes(List.of("three", "four")) + .refreshTokenTimeSkew(Duration.ofSeconds(987)) + .accessTokenExpiresIn(Duration.ofSeconds(789)) + .absoluteExpiresIn(true) + .grant() + .type(Type.CODE) + .accessTokenProperty("access_token_test") + .refreshTokenProperty("refresh_token_test") + .expiresInProperty("expires_in_test") + .refreshExpiresInProperty("refresh_expires_in_test") + .end() + .grantOptions("one", "two", "three") + .grantOptions("four", Map.of("five", "six")) + .grantOptions(Map.of("seven", Map.of("eight", "nine"))) + .earlyTokensAcquisition(false) + .headers("one", "two") + .headers(Map.of("three", "four")) + .build(); + + // OidcClientConfig methods + assertEquals("test-copy-client-props", existingConfig.id().orElse(null)); + assertFalse(existingConfig.clientEnabled()); + assertTrue(existingConfig.scopes().isPresent()); + List scopes = existingConfig.scopes().get(); + assertEquals(4, scopes.size()); + assertTrue(scopes.contains("one")); + assertTrue(scopes.contains("two")); + assertTrue(scopes.contains("three")); + assertTrue(scopes.contains("four")); + assertEquals(987, existingConfig.refreshTokenTimeSkew().map(Duration::getSeconds).orElse(-1L)); + assertEquals(789, existingConfig.accessTokenExpiresIn().map(Duration::getSeconds).orElse(-1L)); + assertTrue(existingConfig.absoluteExpiresIn()); + var grant = existingConfig.grant(); + assertNotNull(grant); + assertEquals(Type.CODE, grant.type()); + assertEquals("access_token_test", grant.accessTokenProperty()); + assertEquals("refresh_token_test", grant.refreshTokenProperty()); + assertEquals("expires_in_test", grant.expiresInProperty()); + assertEquals("refresh_expires_in_test", grant.refreshExpiresInProperty()); + var grantOptions = existingConfig.grantOptions(); + assertNotNull(grantOptions); + assertEquals(3, grantOptions.size()); + assertTrue(grantOptions.containsKey("one")); + assertEquals("three", grantOptions.get("one").get("two")); + assertTrue(grantOptions.containsKey("four")); + assertEquals("six", grantOptions.get("four").get("five")); + assertTrue(grantOptions.containsKey("seven")); + assertEquals("nine", grantOptions.get("seven").get("eight")); + assertFalse(existingConfig.earlyTokensAcquisition()); + var headers = existingConfig.headers(); + assertNotNull(headers); + assertEquals(2, headers.size()); + assertTrue(headers.containsKey("one")); + assertEquals("two", headers.get("one")); + assertTrue(headers.containsKey("three")); + assertEquals("four", headers.get("three")); + + var newConfig = OidcClientConfig.builder(existingConfig) + // OidcClientConfig methods + .clientEnabled(true) + .scopes("five", "six") + .accessTokenExpiresIn(Duration.ofSeconds(444)) + .grant() + .accessTokenProperty("access_token_test-CHANGED") + .expiresInProperty("expires_in_test-CHANGED") + .end() + .earlyTokensAcquisition(true) + .build(); + + // OidcClientConfig methods + assertEquals("test-copy-client-props", newConfig.id().orElse(null)); + assertTrue(newConfig.clientEnabled()); + assertTrue(newConfig.scopes().isPresent()); + scopes = newConfig.scopes().get(); + assertEquals(6, scopes.size()); + assertTrue(scopes.contains("one")); + assertTrue(scopes.contains("two")); + assertTrue(scopes.contains("three")); + assertTrue(scopes.contains("four")); + assertTrue(scopes.contains("five")); + assertTrue(scopes.contains("six")); + assertEquals(987, newConfig.refreshTokenTimeSkew().map(Duration::getSeconds).orElse(-1L)); + assertEquals(444, newConfig.accessTokenExpiresIn().map(Duration::getSeconds).orElse(-1L)); + assertTrue(newConfig.absoluteExpiresIn()); + grant = newConfig.grant(); + assertNotNull(grant); + assertEquals(Type.CODE, grant.type()); + assertEquals("access_token_test-CHANGED", grant.accessTokenProperty()); + assertEquals("refresh_token_test", grant.refreshTokenProperty()); + assertEquals("expires_in_test-CHANGED", grant.expiresInProperty()); + assertEquals("refresh_expires_in_test", grant.refreshExpiresInProperty()); + grantOptions = newConfig.grantOptions(); + assertNotNull(grantOptions); + assertEquals(3, grantOptions.size()); + assertTrue(grantOptions.containsKey("one")); + assertEquals("three", grantOptions.get("one").get("two")); + assertTrue(grantOptions.containsKey("four")); + assertEquals("six", grantOptions.get("four").get("five")); + assertTrue(grantOptions.containsKey("seven")); + assertEquals("nine", grantOptions.get("seven").get("eight")); + assertTrue(newConfig.earlyTokensAcquisition()); + headers = newConfig.headers(); + assertNotNull(headers); + assertEquals(2, headers.size()); + assertTrue(headers.containsKey("one")); + assertEquals("two", headers.get("one")); + assertTrue(headers.containsKey("three")); + assertEquals("four", headers.get("three")); + } + + @Test + public void testCopyOidcClientCommonConfigProperties() { + var existingConfig = OidcClientConfig.builder() + // OidcClientConfig methods + .id("copy-oidc-client-common-props") + // OidcClientCommonConfig methods + .tokenPath("token-path-yep") + .revokePath("revoke-path-yep") + .clientId("client-id-yep") + .clientName("client-name-yep") + .credentials() + .secret("secret-yep") + .clientSecret() + .method(Method.QUERY) + .value("value-yep") + .provider("key-yep", "name-yep", "keyring-name-yep") + .end() + .jwt() + .source(Source.BEARER) + .secretProvider() + .keyringName("jwt-keyring-name-yep") + .key("jwt-key-yep") + .name("jwt-name-yep") + .end() + .secret("jwt-secret-yep") + .key("jwt-key-yep") + .keyFile("jwt-key-file-yep") + .keyStoreFile("jwt-key-store-file-yep") + .keyStorePassword("jwt-key-store-password-yep") + .keyId("jwt-key-id-yep") + .keyPassword("jwt-key-pwd-yep") + .audience("jwt-audience-yep") + .tokenKeyId("jwt-token-key-id-yep") + .issuer("jwt-issuer") + .subject("jwt-subject") + .claim("claim-one-name", "claim-one-value") + .claims(Map.of("claim-two-name", "claim-two-value")) + .signatureAlgorithm("ES512") + .lifespan(852) + .assertion(true) + .endCredentials() + .build(); + + assertEquals("copy-oidc-client-common-props", existingConfig.id().orElse(null)); + + // OidcClientCommonConfig methods + assertEquals("token-path-yep", existingConfig.tokenPath().orElse(null)); + assertEquals("revoke-path-yep", existingConfig.revokePath().orElse(null)); + assertEquals("client-id-yep", existingConfig.clientId().orElse(null)); + assertEquals("client-name-yep", existingConfig.clientName().orElse(null)); + var credentials = existingConfig.credentials(); + assertNotNull(credentials); + assertEquals("secret-yep", credentials.secret().orElse(null)); + var clientSecret = credentials.clientSecret(); + assertNotNull(clientSecret); + assertEquals(Method.QUERY, clientSecret.method().orElse(null)); + assertEquals("value-yep", clientSecret.value().orElse(null)); + var provider = clientSecret.provider(); + assertNotNull(provider); + assertEquals("key-yep", provider.key().orElse(null)); + assertEquals("name-yep", provider.name().orElse(null)); + assertEquals("keyring-name-yep", provider.keyringName().orElse(null)); + var jwt = credentials.jwt(); + assertNotNull(jwt); + assertEquals(Source.BEARER, jwt.source()); + assertEquals("jwt-secret-yep", jwt.secret().orElse(null)); + provider = jwt.secretProvider(); + assertNotNull(provider); + assertEquals("jwt-keyring-name-yep", provider.keyringName().orElse(null)); + assertEquals("jwt-name-yep", provider.name().orElse(null)); + assertEquals("jwt-key-yep", provider.key().orElse(null)); + assertEquals("jwt-key-yep", jwt.key().orElse(null)); + assertEquals("jwt-key-file-yep", jwt.keyFile().orElse(null)); + assertEquals("jwt-key-store-file-yep", jwt.keyStoreFile().orElse(null)); + assertEquals("jwt-key-store-password-yep", jwt.keyStorePassword().orElse(null)); + assertEquals("jwt-key-id-yep", jwt.keyId().orElse(null)); + assertEquals("jwt-key-pwd-yep", jwt.keyPassword().orElse(null)); + assertEquals("jwt-audience-yep", jwt.audience().orElse(null)); + assertEquals("jwt-token-key-id-yep", jwt.tokenKeyId().orElse(null)); + assertEquals("jwt-issuer", jwt.issuer().orElse(null)); + assertEquals("jwt-subject", jwt.subject().orElse(null)); + var claims = jwt.claims(); + assertNotNull(claims); + assertEquals(2, claims.size()); + assertTrue(claims.containsKey("claim-one-name")); + assertEquals("claim-one-value", claims.get("claim-one-name")); + assertTrue(claims.containsKey("claim-two-name")); + assertEquals("claim-two-value", claims.get("claim-two-name")); + assertEquals("ES512", jwt.signatureAlgorithm().orElse(null)); + assertEquals(852, jwt.lifespan()); + assertTrue(jwt.assertion()); + + var newConfig = OidcClientConfig.builder(existingConfig) + // OidcClientCommonConfig methods + .tokenPath("token-path-yep-CHANGED") + .clientId("client-id-yep-CHANGED") + .credentials() + .secret("secret-yep-CHANGED") + .clientSecret("val-1", Method.POST_JWT) + .jwt() + .secret("different-secret") + .secretProvider() + .key("jwt-key-yep-CHANGED") + .end() + .key("jwt-key-yep-CHANGED-2") + .keyStoreFile("jwt-key-store-file-yep-CHANGED") + .keyPassword("jwt-key-pwd-yep-CHANGED") + .issuer("jwt-issuer-CHANGED") + .claim("aaa", "bbb") + .lifespan(333) + .end() + .clientSecret("val-1", Method.POST_JWT) + .end() + .build(); + + assertEquals("copy-oidc-client-common-props", newConfig.id().orElse(null)); + + // OidcClientCommonConfig methods + assertEquals("token-path-yep-CHANGED", newConfig.tokenPath().orElse(null)); + assertEquals("revoke-path-yep", newConfig.revokePath().orElse(null)); + assertEquals("client-id-yep-CHANGED", newConfig.clientId().orElse(null)); + assertEquals("client-name-yep", newConfig.clientName().orElse(null)); + credentials = newConfig.credentials(); + assertNotNull(credentials); + assertEquals("secret-yep-CHANGED", credentials.secret().orElse(null)); + clientSecret = credentials.clientSecret(); + assertNotNull(clientSecret); + assertEquals(Method.POST_JWT, clientSecret.method().orElse(null)); + assertEquals("val-1", clientSecret.value().orElse(null)); + provider = clientSecret.provider(); + assertNotNull(provider); + assertEquals("key-yep", provider.key().orElse(null)); + assertEquals("name-yep", provider.name().orElse(null)); + assertEquals("keyring-name-yep", provider.keyringName().orElse(null)); + jwt = credentials.jwt(); + assertNotNull(jwt); + assertEquals(Source.BEARER, jwt.source()); + assertEquals("different-secret", jwt.secret().orElse(null)); + provider = jwt.secretProvider(); + assertNotNull(provider); + assertEquals("jwt-keyring-name-yep", provider.keyringName().orElse(null)); + assertEquals("jwt-name-yep", provider.name().orElse(null)); + assertEquals("jwt-key-yep-CHANGED", provider.key().orElse(null)); + assertEquals("jwt-key-yep-CHANGED-2", jwt.key().orElse(null)); + assertEquals("jwt-key-file-yep", jwt.keyFile().orElse(null)); + assertEquals("jwt-key-store-file-yep-CHANGED", jwt.keyStoreFile().orElse(null)); + assertEquals("jwt-key-store-password-yep", jwt.keyStorePassword().orElse(null)); + assertEquals("jwt-key-id-yep", jwt.keyId().orElse(null)); + assertEquals("jwt-key-pwd-yep-CHANGED", jwt.keyPassword().orElse(null)); + assertEquals("jwt-audience-yep", jwt.audience().orElse(null)); + assertEquals("jwt-token-key-id-yep", jwt.tokenKeyId().orElse(null)); + assertEquals("jwt-issuer-CHANGED", jwt.issuer().orElse(null)); + assertEquals("jwt-subject", jwt.subject().orElse(null)); + claims = jwt.claims(); + assertNotNull(claims); + assertEquals(3, claims.size()); + assertTrue(claims.containsKey("claim-one-name")); + assertEquals("claim-one-value", claims.get("claim-one-name")); + assertTrue(claims.containsKey("claim-two-name")); + assertEquals("claim-two-value", claims.get("claim-two-name")); + assertTrue(claims.containsKey("aaa")); + assertEquals("bbb", claims.get("aaa")); + assertEquals("ES512", jwt.signatureAlgorithm().orElse(null)); + assertEquals(333, jwt.lifespan()); + assertTrue(jwt.assertion()); + } + + @Test + public void testCopyOidcCommonConfigProperties() { + var previousConfig = OidcClientConfig.builder() + .id("common-props-test") + .authServerUrl("we") + .discoveryEnabled(false) + .registrationPath("don't") + .connectionDelay(Duration.ofSeconds(656)) + .connectionRetryCount(565) + .connectionTimeout(Duration.ofSeconds(673)) + .useBlockingDnsLookup(true) + .maxPoolSize(376) + .followRedirects(false) + .proxy("need", 55, "no", "education") + .tlsConfigurationName("Teacher!") + .build(); + var newConfig = OidcClientConfig.builder(previousConfig) + .discoveryEnabled(true) + .connectionDelay(Duration.ofSeconds(753)) + .connectionTimeout(Duration.ofSeconds(357)) + .maxPoolSize(1988) + .proxy("cross", 44, "the", "boarder") + .build(); + + assertEquals("common-props-test", newConfig.id().orElse(null)); + assertEquals("we", newConfig.authServerUrl().orElse(null)); + assertTrue(newConfig.discoveryEnabled().orElse(false)); + assertEquals("don't", newConfig.registrationPath().orElse(null)); + assertEquals(753, newConfig.connectionDelay().map(Duration::getSeconds).orElse(null)); + assertEquals(565, newConfig.connectionRetryCount()); + assertEquals(357, newConfig.connectionTimeout().getSeconds()); + assertTrue(newConfig.useBlockingDnsLookup()); + assertEquals(1988, newConfig.maxPoolSize().orElse(0)); + assertFalse(newConfig.followRedirects()); + assertNotNull(newConfig.proxy()); + assertEquals("cross", newConfig.proxy().host().orElse(null)); + assertEquals(44, newConfig.proxy().port()); + assertEquals("the", newConfig.proxy().username().orElse(null)); + assertEquals("boarder", newConfig.proxy().password().orElse(null)); + assertNotNull(newConfig.tls()); + assertEquals("Teacher!", newConfig.tls().tlsConfigurationName().orElse(null)); + assertTrue(newConfig.tls().verification().isEmpty()); + assertTrue(newConfig.tls().keyStoreFile().isEmpty()); + assertTrue(newConfig.tls().keyStoreFileType().isEmpty()); + assertTrue(newConfig.tls().keyStoreProvider().isEmpty()); + assertTrue(newConfig.tls().keyStorePassword().isEmpty()); + assertTrue(newConfig.tls().keyStoreKeyAlias().isEmpty()); + assertTrue(newConfig.tls().keyStoreKeyPassword().isEmpty()); + assertTrue(newConfig.tls().trustStoreFile().isEmpty()); + assertTrue(newConfig.tls().trustStorePassword().isEmpty()); + assertTrue(newConfig.tls().trustStoreCertAlias().isEmpty()); + assertTrue(newConfig.tls().trustStoreFileType().isEmpty()); + assertTrue(newConfig.tls().trustStoreProvider().isEmpty()); + } + + @Test + public void testCreateBuilderShortcuts() { + OidcClientConfig config = OidcClientConfig.authServerUrl("auth-server-url").id("shortcuts-1").build(); + assertEquals("auth-server-url", config.authServerUrl().orElse(null)); + assertEquals("shortcuts-1", config.id().orElse(null)); + + config = OidcClientConfig.registrationPath("registration-path").id("shortcuts-2").build(); + assertEquals("registration-path", config.registrationPath().orElse(null)); + assertEquals("shortcuts-2", config.id().orElse(null)); + + config = OidcClientConfig.tokenPath("token-path").id("shortcuts-3").build(); + assertEquals("token-path", config.tokenPath().orElse(null)); + assertEquals("shortcuts-3", config.id().orElse(null)); + } + + @Test + public void testCredentialsBuilder() { + var jwt = new JwtBuilder<>() + .secret("hush-hush") + .build(); + var clientSecret = new SecretBuilder<>() + .value("harry") + .build(); + var credentials = new CredentialsBuilder<>() + .secret("1234") + .jwt(jwt) + .clientSecret(clientSecret) + .build(); + var config = OidcClientConfig.builder().id("1").credentials(credentials).build(); + var buildCredentials = config.credentials(); + assertEquals("1", config.id().orElse(null)); + assertNotNull(buildCredentials); + assertEquals("1234", buildCredentials.secret().orElse(null)); + assertEquals("hush-hush", buildCredentials.jwt().secret().orElse(null)); + assertEquals("harry", buildCredentials.clientSecret().value().orElse(null)); + } + + @Test + public void testGrantBuilder() { + var grant = new OidcClientConfigBuilder.GrantBuilder().build(); + // tests defaults + assertEquals(Type.CLIENT, grant.type()); + assertEquals(OidcConstants.ACCESS_TOKEN_VALUE, grant.accessTokenProperty()); + assertEquals(OidcConstants.REFRESH_EXPIRES_IN, grant.refreshExpiresInProperty()); + assertEquals(OidcConstants.REFRESH_TOKEN_VALUE, grant.refreshTokenProperty()); + assertEquals(OidcConstants.EXPIRES_IN, grant.expiresInProperty()); + + grant = new OidcClientConfigBuilder.GrantBuilder() + .type(Type.CIBA) + .expiresInProperty("exp1") + .accessTokenProperty("acc1") + .refreshExpiresInProperty("exp2") + .refreshTokenProperty("ref1") + .build(); + var config = OidcClientConfig.builder().id("2").grant(grant).build(); + var buildGrant = config.grant(); + assertEquals("2", config.id().orElse(null)); + assertNotNull(buildGrant); + assertEquals(Type.CIBA, buildGrant.type()); + assertEquals("exp1", buildGrant.expiresInProperty()); + assertEquals("acc1", buildGrant.accessTokenProperty()); + assertEquals("exp2", buildGrant.refreshExpiresInProperty()); + assertEquals("ref1", buildGrant.refreshTokenProperty()); + } +} diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcClientCommonConfig.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcClientCommonConfig.java index 619982e2bf395..d0d2040b10858 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcClientCommonConfig.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcClientCommonConfig.java @@ -4,9 +4,10 @@ import java.util.Map; import java.util.Optional; -public class OidcClientCommonConfig extends OidcCommonConfig { +public abstract class OidcClientCommonConfig extends OidcCommonConfig + implements io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig { - public OidcClientCommonConfig() { + protected OidcClientCommonConfig() { } @@ -50,7 +51,32 @@ protected OidcClientCommonConfig(io.quarkus.oidc.common.runtime.config.OidcClien */ public Credentials credentials = new Credentials(); - public static class Credentials { + @Override + public Optional tokenPath() { + return tokenPath; + } + + @Override + public Optional revokePath() { + return revokePath; + } + + @Override + public Optional clientId() { + return clientId; + } + + @Override + public Optional clientName() { + return clientName; + } + + @Override + public io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials credentials() { + return credentials; + } + + public static class Credentials implements io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials { /** * The client secret used by the `client_secret_basic` authentication method. @@ -102,13 +128,44 @@ private void addConfigMappingValues(io.quarkus.oidc.common.runtime.config.OidcCl jwt.addConfigMappingValues(mapping.jwt()); } + @Override + public Optional secret() { + return secret; + } + + @Override + public io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret clientSecret() { + return clientSecret; + } + + @Override + public io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt jwt() { + return jwt; + } + /** * Supports the client authentication methods that involve sending a client secret. * * @see https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication */ - public static class Secret { + public static class Secret implements io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret { + + @Override + public Optional value() { + return value; + } + + @Override + public io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Provider provider() { + return provider; + } + + @Override + public Optional method() { + return method.map(Enum::toString) + .map(io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret.Method::valueOf); + } public static enum Method { /** @@ -194,7 +251,94 @@ private void addConfigMappingValues( * @see https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication */ - public static class Jwt { + public static class Jwt implements io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt { + + @Override + public io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt.Source source() { + return source == null ? null + : io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt.Source + .valueOf(source.toString()); + } + + @Override + public Optional secret() { + return secret; + } + + @Override + public io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Provider secretProvider() { + return secretProvider; + } + + @Override + public Optional key() { + return key; + } + + @Override + public Optional keyFile() { + return keyFile; + } + + @Override + public Optional keyStoreFile() { + return keyStoreFile; + } + + @Override + public Optional keyStorePassword() { + return keyStorePassword; + } + + @Override + public Optional keyId() { + return keyId; + } + + @Override + public Optional keyPassword() { + return keyPassword; + } + + @Override + public Optional audience() { + return audience; + } + + @Override + public Optional tokenKeyId() { + return tokenKeyId; + } + + @Override + public Optional issuer() { + return issuer; + } + + @Override + public Optional subject() { + return subject; + } + + @Override + public Map claims() { + return claims; + } + + @Override + public Optional signatureAlgorithm() { + return signatureAlgorithm; + } + + @Override + public int lifespan() { + return lifespan; + } + + @Override + public boolean assertion() { + return assertion; + } public static enum Source { // JWT token is generated by the OIDC provider client to support @@ -417,7 +561,8 @@ private void addConfigMappingValues( /** * CredentialsProvider, which provides a client secret. */ - public static class Provider { + public static class Provider + implements io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Provider { /** * The CredentialsProvider bean name, which should only be set if more than one CredentialsProvider is @@ -469,6 +614,21 @@ private void addConfigMappingValues( keyringName = mapping.keyringName(); key = mapping.key(); } + + @Override + public Optional name() { + return name; + } + + @Override + public Optional keyringName() { + return keyringName; + } + + @Override + public Optional key() { + return key; + } } } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java index 4b1e5956e0788..3be6c684f063d 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java @@ -47,10 +47,11 @@ import io.quarkus.oidc.common.OidcRequestFilter.OidcRequestContext; import io.quarkus.oidc.common.OidcResponseFilter; import io.quarkus.oidc.common.OidcResponseFilter.OidcResponseContext; -import io.quarkus.oidc.common.runtime.OidcClientCommonConfig.Credentials; -import io.quarkus.oidc.common.runtime.OidcClientCommonConfig.Credentials.Provider; -import io.quarkus.oidc.common.runtime.OidcClientCommonConfig.Credentials.Secret; import io.quarkus.oidc.common.runtime.OidcTlsSupport.TlsConfigSupport; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Provider; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret; import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.oidc.common.runtime.config.OidcCommonConfig.Tls.Verification; import io.quarkus.runtime.configuration.ConfigurationException; @@ -102,19 +103,19 @@ public static void verifyEndpointUrl(String endpointUrl) { public static void verifyCommonConfiguration(OidcClientCommonConfig oidcConfig, boolean clientIdOptional, boolean isServerConfig) { final String configPrefix = isServerConfig ? "quarkus.oidc." : "quarkus.oidc-client."; - if (!clientIdOptional && !oidcConfig.getClientId().isPresent()) { + if (!clientIdOptional && !oidcConfig.clientId().isPresent()) { throw new ConfigurationException( String.format("'%sclient-id' property must be configured", configPrefix)); } - Credentials creds = oidcConfig.getCredentials(); - if (creds.secret.isPresent() && creds.clientSecret.value.isPresent()) { + Credentials creds = oidcConfig.credentials(); + if (creds.secret().isPresent() && creds.clientSecret().value().isPresent()) { throw new ConfigurationException( String.format( "'%1$scredentials.secret' and '%1$scredentials.client-secret' properties are mutually exclusive", configPrefix)); } - if ((creds.secret.isPresent() || creds.clientSecret.value.isPresent()) && creds.jwt.secret.isPresent()) { + if ((creds.secret().isPresent() || creds.clientSecret().value().isPresent()) && creds.jwt().secret().isPresent()) { throw new ConfigurationException( String.format( "Use only '%1$scredentials.secret' or '%1$scredentials.client-secret' or '%1$scredentials.jwt.secret' property", @@ -299,26 +300,26 @@ public static String formatConnectionErrorMessage(String authServerUrlString) { } public static boolean isClientSecretBasicAuthRequired(Credentials creds) { - return creds.secret.isPresent() || - ((creds.clientSecret.value.isPresent() || creds.clientSecret.provider.key.isPresent()) + return creds.secret().isPresent() || + ((creds.clientSecret().value().isPresent() || creds.clientSecret().provider().key().isPresent()) && clientSecretMethod(creds) == Secret.Method.BASIC); } public static boolean isClientJwtAuthRequired(Credentials creds, boolean server) { Set props = new HashSet<>(); - if (creds.jwt.secret.isPresent()) { + if (creds.jwt().secret().isPresent()) { props.add(".credentials.jwt.secret"); } - if (creds.jwt.secretProvider.key.isPresent()) { + if (creds.jwt().secretProvider().key().isPresent()) { props.add(".credentials.jwt.secret-provider.key"); } - if (creds.jwt.key.isPresent()) { + if (creds.jwt().key().isPresent()) { props.add(".credentials.jwt.key"); } - if (creds.jwt.keyFile.isPresent()) { + if (creds.jwt().keyFile().isPresent()) { props.add(".credentials.jwt.key-file"); } - if (creds.jwt.keyStoreFile.isPresent()) { + if (creds.jwt().keyStoreFile().isPresent()) { props.add(".credentials.jwt.key-store-file"); } if (props.size() > 1) { @@ -331,7 +332,7 @@ public static boolean isClientJwtAuthRequired(Credentials creds, boolean server) } public static boolean isClientSecretPostAuthRequired(Credentials creds) { - return (creds.clientSecret.value.isPresent() || creds.clientSecret.provider.key.isPresent()) + return (creds.clientSecret().value().isPresent() || creds.clientSecret().provider().key().isPresent()) && clientSecretMethod(creds) == Secret.Method.POST; } @@ -340,15 +341,16 @@ public static boolean isClientSecretPostJwtAuthRequired(Credentials creds) { } public static boolean isJwtAssertion(Credentials creds) { - return creds.getJwt().isAssertion(); + return creds.jwt().assertion(); } public static String clientSecret(Credentials creds) { - return creds.secret.orElse(creds.clientSecret.value.orElseGet(fromCredentialsProvider(creds.clientSecret.provider))); + return creds.secret() + .orElse(creds.clientSecret().value().orElseGet(fromCredentialsProvider(creds.clientSecret().provider()))); } public static String jwtSecret(Credentials creds) { - return creds.jwt.secret.orElseGet(fromCredentialsProvider(creds.jwt.secretProvider)); + return creds.jwt().secret().orElseGet(fromCredentialsProvider(creds.jwt().secretProvider())); } public static String getClientOrJwtSecret(Credentials creds) { @@ -369,7 +371,7 @@ public static SecretKey generateSecretKey() throws Exception { } public static Secret.Method clientSecretMethod(Credentials creds) { - return creds.clientSecret.method.orElseGet(() -> Secret.Method.BASIC); + return creds.clientSecret().method().orElseGet(() -> Secret.Method.BASIC); } private static Supplier fromCredentialsProvider(Provider provider) { @@ -377,11 +379,11 @@ private static Supplier fromCredentialsProvider(Provider provi @Override public String get() { - if (provider.key.isPresent()) { - String providerName = provider.name.orElse(null); - String keyringName = provider.keyringName.orElse(null); + if (provider.key().isPresent()) { + String providerName = provider.name().orElse(null); + String keyringName = provider.keyringName().orElse(null); CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(providerName); - return credentialsProvider.getCredentials(keyringName).get(provider.key.get()); + return credentialsProvider.getCredentials(keyringName).get(provider.key().get()); } return null; } @@ -389,31 +391,31 @@ public String get() { } public static Key clientJwtKey(Credentials creds) { - if (creds.jwt.secret.isPresent() || creds.jwt.secretProvider.key.isPresent()) { + if (creds.jwt().secret().isPresent() || creds.jwt().secretProvider().key().isPresent()) { return KeyUtils .createSecretKeyFromSecret(jwtSecret(creds)); } else { Key key = null; try { - if (creds.jwt.getKey().isPresent()) { - key = KeyUtils.tryAsPemSigningPrivateKey(creds.jwt.getKey().get(), + if (creds.jwt().key().isPresent()) { + key = KeyUtils.tryAsPemSigningPrivateKey(creds.jwt().key().get(), getSignatureAlgorithm(creds, SignatureAlgorithm.RS256)); - } else if (creds.jwt.getKeyFile().isPresent()) { - key = KeyUtils.readSigningKey(creds.jwt.getKeyFile().get(), creds.jwt.keyId.orElse(null), + } else if (creds.jwt().keyFile().isPresent()) { + key = KeyUtils.readSigningKey(creds.jwt().keyFile().get(), creds.jwt().keyId().orElse(null), getSignatureAlgorithm(creds, SignatureAlgorithm.RS256)); - } else if (creds.jwt.keyStoreFile.isPresent()) { - var keyStoreFile = creds.jwt.keyStoreFile.get(); + } else if (creds.jwt().keyStoreFile().isPresent()) { + var keyStoreFile = creds.jwt().keyStoreFile().get(); KeyStore ks = KeyStore.getInstance(inferKeyStoreTypeFromFileExtension(keyStoreFile)); InputStream is = ResourceUtils.getResourceStream(keyStoreFile); - if (creds.jwt.keyStorePassword.isPresent()) { - ks.load(is, creds.jwt.keyStorePassword.get().toCharArray()); + if (creds.jwt().keyStorePassword().isPresent()) { + ks.load(is, creds.jwt().keyStorePassword().get().toCharArray()); } else { ks.load(is, null); } - if (creds.jwt.keyPassword.isPresent()) { - key = ks.getKey(creds.jwt.keyId.get(), creds.jwt.keyPassword.get().toCharArray()); + if (creds.jwt().keyPassword().isPresent()) { + key = ks.getKey(creds.jwt().keyId().get(), creds.jwt().keyPassword().get().toCharArray()); } else { throw new ConfigurationException( "When using a key store, the `quarkus.oidc-client.credentials.jwt.key-password` property must be set"); @@ -432,17 +434,17 @@ public static Key clientJwtKey(Credentials creds) { public static String signJwtWithKey(OidcClientCommonConfig oidcConfig, String tokenRequestUri, Key key) { // 'jti' and 'iat' claims are created by default, 'iat' - is set to the current time JwtSignatureBuilder jwtSignatureBuilder = Jwt - .claims(additionalClaims(oidcConfig.credentials.jwt.getClaims())) - .issuer(oidcConfig.credentials.jwt.issuer.orElse(oidcConfig.clientId.get())) - .subject(oidcConfig.credentials.jwt.subject.orElse(oidcConfig.clientId.get())) - .audience(oidcConfig.credentials.jwt.getAudience().isPresent() - ? removeLastPathSeparator(oidcConfig.credentials.jwt.getAudience().get()) + .claims(additionalClaims(oidcConfig.credentials().jwt().claims())) + .issuer(oidcConfig.credentials().jwt().issuer().orElse(oidcConfig.clientId().get())) + .subject(oidcConfig.credentials().jwt().subject().orElse(oidcConfig.clientId().get())) + .audience(oidcConfig.credentials().jwt().audience().isPresent() + ? removeLastPathSeparator(oidcConfig.credentials().jwt().audience().get()) : tokenRequestUri) - .expiresIn(oidcConfig.credentials.jwt.lifespan).jws(); - if (oidcConfig.credentials.jwt.getTokenKeyId().isPresent()) { - jwtSignatureBuilder.keyId(oidcConfig.credentials.jwt.getTokenKeyId().get()); + .expiresIn(oidcConfig.credentials().jwt().lifespan()).jws(); + if (oidcConfig.credentials().jwt().tokenKeyId().isPresent()) { + jwtSignatureBuilder.keyId(oidcConfig.credentials().jwt().tokenKeyId().get()); } - SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(oidcConfig.credentials, null); + SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(oidcConfig.credentials(), null); if (signatureAlgorithm != null) { jwtSignatureBuilder.algorithm(signatureAlgorithm); } @@ -460,9 +462,9 @@ private static Map additionalClaims(Map claims) } private static SignatureAlgorithm getSignatureAlgorithm(Credentials credentials, SignatureAlgorithm defaultAlgorithm) { - if (credentials.jwt.getSignatureAlgorithm().isPresent()) { + if (credentials.jwt().signatureAlgorithm().isPresent()) { try { - return SignatureAlgorithm.fromAlgorithm(credentials.jwt.getSignatureAlgorithm().get()); + return SignatureAlgorithm.fromAlgorithm(credentials.jwt().signatureAlgorithm().get()); } catch (Exception ex) { throw new ConfigurationException("Unsupported signature algorithm"); } @@ -483,8 +485,8 @@ public static void verifyConfigurationId(String defaultId, String configKey, Opt } public static String initClientSecretBasicAuth(OidcClientCommonConfig oidcConfig) { - if (isClientSecretBasicAuthRequired(oidcConfig.credentials)) { - return basicSchemeValue(oidcConfig.getClientId().get(), clientSecret(oidcConfig.credentials)); + if (isClientSecretBasicAuthRequired(oidcConfig.credentials())) { + return basicSchemeValue(oidcConfig.clientId().get(), clientSecret(oidcConfig.credentials())); } return null; } @@ -496,8 +498,8 @@ public static String basicSchemeValue(String name, String secret) { } public static Key initClientJwtKey(OidcClientCommonConfig oidcConfig, boolean server) { - if (isClientJwtAuthRequired(oidcConfig.credentials, server)) { - return clientJwtKey(oidcConfig.credentials); + if (isClientJwtAuthRequired(oidcConfig.credentials(), server)) { + return clientJwtKey(oidcConfig.credentials()); } return null; } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcTlsSupport.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcTlsSupport.java index 98f678532f9ff..8b2ceb1c082ef 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcTlsSupport.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcTlsSupport.java @@ -6,13 +6,14 @@ import javax.net.ssl.SSLContext; +import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.tls.TlsConfiguration; import io.quarkus.tls.TlsConfigurationRegistry; public interface OidcTlsSupport { default TlsConfigSupport forConfig(OidcCommonConfig.Tls config) { - return config == null ? forConfig(Optional.empty()) : forConfig(config.tlsConfigurationName); + return config == null ? forConfig(Optional.empty()) : forConfig(config.tlsConfigurationName()); } TlsConfigSupport forConfig(Optional tlsConfigurationName); diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfigBuilder.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfigBuilder.java new file mode 100644 index 0000000000000..3e3112d0bd858 --- /dev/null +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcClientCommonConfigBuilder.java @@ -0,0 +1,747 @@ +package io.quarkus.oidc.common.runtime.config; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Jwt.Source; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Provider; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret.Method; + +public abstract class OidcClientCommonConfigBuilder extends OidcCommonConfigBuilder { + + protected static class OidcClientCommonConfigImpl extends OidcCommonConfigImpl implements OidcClientCommonConfig { + + private final Optional tokenPath; + private final Optional revokePath; + private final Optional clientId; + private final Optional clientName; + private final Credentials credentials; + + protected OidcClientCommonConfigImpl(OidcClientCommonConfigBuilder builder) { + super(builder); + this.tokenPath = builder.tokenPath; + this.revokePath = builder.revokePath; + this.clientId = builder.clientId; + this.clientName = builder.clientName; + this.credentials = builder.credentials; + } + + @Override + public Optional tokenPath() { + return tokenPath; + } + + @Override + public Optional revokePath() { + return revokePath; + } + + @Override + public Optional clientId() { + return clientId; + } + + @Override + public Optional clientName() { + return clientName; + } + + @Override + public Credentials credentials() { + return credentials; + } + } + + private Optional tokenPath; + private Optional revokePath; + private Optional clientId; + private Optional clientName; + private Credentials credentials; + + protected OidcClientCommonConfigBuilder(OidcClientCommonConfig oidcClientCommonConfig) { + super(oidcClientCommonConfig); + this.tokenPath = oidcClientCommonConfig.tokenPath(); + this.revokePath = oidcClientCommonConfig.revokePath(); + this.clientId = oidcClientCommonConfig.clientId(); + this.clientName = oidcClientCommonConfig.clientName(); + this.credentials = oidcClientCommonConfig.credentials(); + } + + /** + * @param tokenPath {@link OidcClientCommonConfig#tokenPath()} + * @return T builder + */ + public T tokenPath(String tokenPath) { + this.tokenPath = Optional.ofNullable(tokenPath); + return getBuilder(); + } + + /** + * @param revokePath {@link OidcClientCommonConfig#revokePath()} + * @return T builder + */ + public T revokePath(String revokePath) { + this.revokePath = Optional.ofNullable(revokePath); + return getBuilder(); + } + + /** + * @param clientId {@link OidcClientCommonConfig#clientId()} + * @return T builder + */ + public T clientId(String clientId) { + this.clientId = Optional.ofNullable(clientId); + return getBuilder(); + } + + /** + * @param clientName {@link OidcClientCommonConfig#clientName()} + * @return T builder + */ + public T clientName(String clientName) { + this.clientName = Optional.ofNullable(clientName); + return getBuilder(); + } + + /** + * @param credentials {@link OidcClientCommonConfig#credentials()} created with {@link CredentialsBuilder} or SmallRye + * Config + * @return T builder + */ + public T credentials(Credentials credentials) { + this.credentials = Objects.requireNonNull(credentials); + return getBuilder(); + } + + /** + * Creates {@link OidcClientCommonConfig#credentials()} builder. + * + * @return CredentialsBuilder + */ + public CredentialsBuilder credentials() { + return new CredentialsBuilder<>(this); + } + + /** + * @param secret {@link Credentials#secret()} + * @return T builder + */ + public T credentials(String secret) { + return credentials().secret(secret).end(); + } + + /** + * @param clientSecret {@link Credentials#clientSecret()} created with {@link SecretBuilder} or SmallRye Config + * @return T builder + */ + public T credentials(Secret clientSecret) { + Objects.requireNonNull(clientSecret); + return credentials().clientSecret(clientSecret).end(); + } + + /** + * @param jwt {@link Credentials#jwt()} created with {@link JwtBuilder} or SmallRye Config + * @return T builder + */ + public T credentials(Jwt jwt) { + Objects.requireNonNull(jwt); + return credentials().jwt(jwt).end(); + } + + /** + * Builder for the {@link Credentials} config. + */ + public static final class CredentialsBuilder { + + private record CredentialsImpl(Optional secret, Secret clientSecret, Jwt jwt) implements Credentials { + } + + private final OidcClientCommonConfigBuilder builder; + private Optional secret; + private Secret clientSecret; + private Jwt jwt; + + public CredentialsBuilder() { + this.builder = null; + this.secret = Optional.empty(); + this.clientSecret = new SecretBuilder<>().build(); + this.jwt = new JwtBuilder<>().build(); + } + + public CredentialsBuilder(OidcClientCommonConfigBuilder builder) { + this.builder = builder; + this.secret = builder.credentials.secret(); + this.clientSecret = builder.credentials.clientSecret(); + this.jwt = builder.credentials.jwt(); + } + + /** + * @param secret {@link Credentials#secret()} + * @return this builder + */ + public CredentialsBuilder secret(String secret) { + this.secret = Optional.ofNullable(secret); + return this; + } + + /** + * @param clientSecret {@link Credentials#clientSecret()} created with the {@link SecretBuilder} or SmallRye Config + * @return this builder + */ + public CredentialsBuilder clientSecret(Secret clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret); + return this; + } + + /** + * Creates builder for the {@link Credentials#clientSecret()}. + * + * @return SecretBuilder + */ + public SecretBuilder clientSecret() { + return new SecretBuilder<>(this); + } + + /** + * @param value {@link Secret#value()} + * @return this builder + */ + public CredentialsBuilder clientSecret(String value) { + return clientSecret().value(value).end(); + } + + /** + * @param provider {@link Secret#provider()} + * @return this builder + */ + public CredentialsBuilder clientSecret(Provider provider) { + return clientSecret().provider(provider).end(); + } + + /** + * @param value {@link Secret#value()} + * @param method {@link Secret#method()} + * @return this builder + */ + public CredentialsBuilder clientSecret(String value, Method method) { + return clientSecret().value(value).method(method).end(); + } + + /** + * @param jwt {@link Credentials#jwt()} created with the {@link JwtBuilder} or SmallRye Config + * @return this builder + */ + public CredentialsBuilder jwt(Jwt jwt) { + this.jwt = Objects.requireNonNull(jwt); + return this; + } + + /** + * Creates builder for the {@link Credentials#jwt()}. + * + * @return JwtBuilder + */ + public JwtBuilder jwt() { + return new JwtBuilder<>(this); + } + + /** + * Builds {@link Credentials} and returns the builder. + * + * @return T builder + */ + public T end() { + Objects.requireNonNull(builder); + return builder.credentials(build()); + } + + /** + * @return Credentials + */ + public Credentials build() { + return new CredentialsImpl(secret, clientSecret, jwt); + } + } + + /** + * The {@link Secret} builder. + */ + public static final class SecretBuilder { + + private record SecretImpl(Optional value, Optional method, Provider provider) implements Secret { + } + + private final CredentialsBuilder builder; + + private Optional value; + private Optional method; + private Provider provider; + + public SecretBuilder() { + this.builder = null; + this.value = Optional.empty(); + this.method = Optional.empty(); + this.provider = new ProviderBuilder<>().build(); + } + + public SecretBuilder(CredentialsBuilder builder) { + this.builder = Objects.requireNonNull(builder); + this.value = builder.clientSecret.value(); + this.method = builder.clientSecret.method(); + this.provider = builder.clientSecret.provider(); + } + + /** + * @param method {@link Secret#method()} + * @return this builder + */ + public SecretBuilder method(Method method) { + this.method = Optional.ofNullable(method); + return this; + } + + /** + * @param value {@link Secret#value()} + * @return this builder + */ + public SecretBuilder value(String value) { + this.value = Optional.ofNullable(value); + return this; + } + + /** + * @param provider {@link Secret#provider()} created with the {@link ProviderBuilder} or SmallRye Config + * @return this builder + */ + public SecretBuilder provider(Provider provider) { + this.provider = Objects.requireNonNull(provider); + return this; + } + + /** + * Adds {@link Secret#provider()}. + * + * @param key {@link Provider#key()} + * @return this builder + */ + public SecretBuilder provider(String key) { + return provider().key(key).end(); + } + + /** + * Adds {@link Secret#provider()}. + * + * @param key {@link Provider#key()} + * @param name {@link Provider#name()} + * @return this builder + */ + public SecretBuilder provider(String key, String name) { + return provider().key(key).name(name).end(); + } + + /** + * Adds {@link Secret#provider()}. + * + * @param key {@link Provider#key()} + * @param name {@link Provider#name()} + * @param keyringName {@link Provider#keyringName()} + * @return this builder + */ + public SecretBuilder provider(String key, String name, String keyringName) { + return provider().key(key).name(name).keyringName(keyringName).end(); + } + + /** + * Creates {@link Secret#provider()} builder. + * + * @return ProviderBuilder + */ + public ProviderBuilder> provider() { + return new ProviderBuilder<>(this::provider, provider); + } + + /** + * Builds {@link Secret} client secret. + * + * @return CredentialsBuilder + */ + public CredentialsBuilder end() { + Objects.requireNonNull(builder); + return builder.clientSecret(build()); + } + + /** + * Builds {@link Credentials#clientSecret()} and {@link OidcClientCommonConfig#credentials()}. + * + * @return T builder + */ + public T endCredentials() { + return end().end(); + } + + public Secret build() { + return new SecretImpl(value, method, provider); + } + } + + /** + * The {@link Provider} builder. + */ + public static final class ProviderBuilder { + + private record ProviderImpl(Optional name, Optional keyringName, + Optional key) implements Provider { + } + + private final Function providerSetter; + private Optional name; + private Optional keyringName; + private Optional key; + + private ProviderBuilder(Function providerSetter, Provider provider) { + this.providerSetter = providerSetter; + this.name = provider.name(); + this.keyringName = provider.keyringName(); + this.key = provider.key(); + } + + public ProviderBuilder() { + this.providerSetter = null; + this.name = Optional.empty(); + this.keyringName = Optional.empty(); + this.key = Optional.empty(); + } + + /** + * @param name {@link Provider#name()} + * @return this builder + */ + public ProviderBuilder name(String name) { + this.name = Optional.ofNullable(name); + return this; + } + + /** + * @param keyringName {@link Provider#keyringName()} + * @return this builder + */ + public ProviderBuilder keyringName(String keyringName) { + this.keyringName = Optional.ofNullable(keyringName); + return this; + } + + /** + * @param key {@link Provider#key()} + * @return this builder + */ + public ProviderBuilder key(String key) { + this.key = Optional.ofNullable(key); + return this; + } + + /** + * Builds {@link Provider}. + * + * @return T builder + */ + public T end() { + Objects.requireNonNull(providerSetter); + return providerSetter.apply(build()); + } + + /** + * Builds {@link Provider}. + * + * @return Provider + */ + public Provider build() { + return new ProviderImpl(name, keyringName, key); + } + + } + + public static final class JwtBuilder { + + private record JwtImpl(Source source, Optional secret, Provider secretProvider, Optional key, + Optional keyFile, Optional keyStoreFile, Optional keyStorePassword, + Optional keyId, Optional keyPassword, Optional audience, Optional tokenKeyId, + Optional issuer, Optional subject, Map claims, + Optional signatureAlgorithm, int lifespan, boolean assertion) implements Jwt { + + } + + private final CredentialsBuilder builder; + private final Map claims = new HashMap<>(); + private Source source; + private Optional secret; + private Provider secretProvider; + private Optional key; + private Optional keyFile; + private Optional keyStoreFile; + private Optional keyStorePassword; + private Optional keyId; + private Optional keyPassword; + private Optional audience; + private Optional tokenKeyId; + private Optional issuer; + private Optional subject; + private Optional signatureAlgorithm; + private int lifespan; + private boolean assertion; + + public JwtBuilder() { + this.builder = null; + this.source = Source.CLIENT; + this.secret = Optional.empty(); + this.secretProvider = new ProviderBuilder<>().build(); + this.key = Optional.empty(); + this.keyFile = Optional.empty(); + this.keyStoreFile = Optional.empty(); + this.keyStorePassword = Optional.empty(); + this.keyId = Optional.empty(); + this.keyPassword = Optional.empty(); + this.audience = Optional.empty(); + this.tokenKeyId = Optional.empty(); + this.issuer = Optional.empty(); + this.subject = Optional.empty(); + this.signatureAlgorithm = Optional.empty(); + this.lifespan = 10; + this.assertion = false; + } + + public JwtBuilder(CredentialsBuilder builder) { + this(Objects.requireNonNull(builder), builder.jwt); + } + + private JwtBuilder(CredentialsBuilder builder, Jwt jwt) { + this.builder = builder; + this.source = jwt.source(); + this.secret = jwt.secret(); + this.secretProvider = jwt.secretProvider(); + this.key = jwt.key(); + this.keyFile = jwt.keyFile(); + this.keyStoreFile = jwt.keyStoreFile(); + this.keyStorePassword = jwt.keyStorePassword(); + this.keyId = jwt.keyId(); + this.keyPassword = jwt.keyPassword(); + this.audience = jwt.audience(); + this.tokenKeyId = jwt.tokenKeyId(); + this.issuer = jwt.issuer(); + this.subject = jwt.subject(); + this.claims.putAll(jwt.claims()); + this.signatureAlgorithm = jwt.signatureAlgorithm(); + this.lifespan = jwt.lifespan(); + this.assertion = jwt.assertion(); + } + + /** + * @return {@link Jwt#secretProvider()} builder + */ + public ProviderBuilder> secretProvider() { + return new ProviderBuilder<>(this::secretProvider, secretProvider); + } + + /** + * @param secretProvider {@link Jwt#secretProvider()} created by {@link ProviderBuilder} or SmallRye Config + * @return this builder + */ + public JwtBuilder secretProvider(Provider secretProvider) { + Objects.requireNonNull(secretProvider); + this.secretProvider = secretProvider; + return this; + } + + /** + * @param source {@link Jwt#source()} + * @return this builder + */ + public JwtBuilder source(Source source) { + Objects.requireNonNull(source); + this.source = source; + return this; + } + + /** + * @param secret {@link Jwt#secret()} + * @return this builder + */ + public JwtBuilder secret(String secret) { + this.secret = Optional.ofNullable(secret); + return this; + } + + /** + * @param key {@link Jwt#key()} + * @return this builder + */ + public JwtBuilder key(String key) { + this.key = Optional.ofNullable(key); + return this; + } + + /** + * @param keyFile {@link Jwt#keyFile()} + * @return this builder + */ + public JwtBuilder keyFile(String keyFile) { + this.keyFile = Optional.ofNullable(keyFile); + return this; + } + + /** + * @param keyStoreFile {@link Jwt#keyStoreFile()} + * @return this builder + */ + public JwtBuilder keyStoreFile(String keyStoreFile) { + this.keyStoreFile = Optional.ofNullable(keyStoreFile); + return this; + } + + /** + * @param keyStorePassword {@link Jwt#keyStorePassword()} + * @return this builder + */ + public JwtBuilder keyStorePassword(String keyStorePassword) { + this.keyStorePassword = Optional.ofNullable(keyStorePassword); + return this; + } + + /** + * @param keyId {@link Jwt#keyId()} + * @return this builder + */ + public JwtBuilder keyId(String keyId) { + this.keyId = Optional.ofNullable(keyId); + return this; + } + + /** + * @param keyPassword {@link Jwt#keyPassword()} + * @return this builder + */ + public JwtBuilder keyPassword(String keyPassword) { + this.keyPassword = Optional.ofNullable(keyPassword); + return this; + } + + /** + * @param audience {@link Jwt#audience()} + * @return this builder + */ + public JwtBuilder audience(String audience) { + this.audience = Optional.ofNullable(audience); + return this; + } + + /** + * @param tokenKeyId {@link Jwt#tokenKeyId()} + * @return this builder + */ + public JwtBuilder tokenKeyId(String tokenKeyId) { + this.tokenKeyId = Optional.ofNullable(tokenKeyId); + return this; + } + + /** + * @param issuer {@link Jwt#issuer()} + * @return this builder + */ + public JwtBuilder issuer(String issuer) { + this.issuer = Optional.ofNullable(issuer); + return this; + } + + /** + * @param subject {@link Jwt#subject()} + * @return this builder + */ + public JwtBuilder subject(String subject) { + this.subject = Optional.ofNullable(subject); + return this; + } + + /** + * @param claimName {@link Jwt#claims()} map entry key + * @param claimValue {@link Jwt#claims()} map entry value + * @return this builder + */ + public JwtBuilder claim(String claimName, String claimValue) { + Objects.requireNonNull(claimName); + Objects.requireNonNull(claimValue); + this.claims.put(claimName, claimValue); + return this; + } + + /** + * @param claims {@link Jwt#claims()} + * @return this builder + */ + public JwtBuilder claims(Map claims) { + Objects.requireNonNull(claims); + this.claims.putAll(claims); + return this; + } + + /** + * @param signatureAlgorithm {@link Jwt#signatureAlgorithm()} + * @return this builder + */ + public JwtBuilder signatureAlgorithm(String signatureAlgorithm) { + this.signatureAlgorithm = Optional.ofNullable(signatureAlgorithm); + return this; + } + + /** + * @param lifespan {@link Jwt#lifespan()} + * @return this builder + */ + public JwtBuilder lifespan(int lifespan) { + this.lifespan = lifespan; + return this; + } + + /** + * @param assertion {@link Jwt#assertion()} + * @return this builder + */ + public JwtBuilder assertion(boolean assertion) { + this.assertion = assertion; + return this; + } + + /** + * Builds {@link Credentials#jwt()}. + * + * @return CredentialsBuilder + */ + public CredentialsBuilder end() { + Objects.requireNonNull(builder); + return builder.jwt(build()); + } + + /** + * Builds {@link Credentials#jwt()} and {@link OidcClientCommonConfig#credentials()}. + * + * @return T builder + */ + public T endCredentials() { + return end().end(); + } + + /** + * Builds {@link Jwt}. + * + * @return Jwt + */ + public Jwt build() { + return new JwtImpl(source, secret, secretProvider, key, keyFile, keyStoreFile, keyStorePassword, keyId, keyPassword, + audience, tokenKeyId, issuer, subject, Map.copyOf(claims), signatureAlgorithm, lifespan, assertion); + } + } +} diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcCommonConfigBuilder.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcCommonConfigBuilder.java index 9a6204a7444d4..5da1c191a9ec6 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcCommonConfigBuilder.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/config/OidcCommonConfigBuilder.java @@ -37,7 +37,7 @@ protected static class OidcCommonConfigImpl implements OidcCommonConfig { private final Proxy proxy; private final Tls tls; - protected OidcCommonConfigImpl(OidcCommonConfigBuilder builder) { + protected OidcCommonConfigImpl(OidcCommonConfigBuilder builder) { this.authServerUrl = builder.authServerUrl; this.discoveryEnabled = builder.discoveryEnabled; this.registrationPath = builder.registrationPath; diff --git a/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java b/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java index 43cdb78f429a7..0125a7013e8f1 100644 --- a/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java +++ b/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java @@ -51,7 +51,8 @@ public void testProxyOptionsWithHostWithScheme() throws Exception { @Test public void testJwtTokenWithScope() throws Exception { - OidcClientCommonConfig cfg = new OidcClientCommonConfig(); + OidcClientCommonConfig cfg = new OidcClientCommonConfig() { + }; cfg.setClientId("client"); cfg.credentials.jwt.claims.put("scope", "read,write"); PrivateKey key = KeyPairGenerator.getInstance("RSA").generateKeyPair().getPrivate(); diff --git a/extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java b/extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java index 8dd14aacbfb00..9f3b7c601061e 100644 --- a/extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java +++ b/extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java @@ -21,9 +21,9 @@ import io.quarkus.arc.Arc; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig.Grant; import io.quarkus.oidc.client.OidcClients; import io.quarkus.oidc.client.runtime.DisabledOidcClientException; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant; import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.security.credential.TokenCredential; import io.quarkus.vertx.core.runtime.context.VertxContextSafetyToggle; diff --git a/extensions/oidc-token-propagation/runtime/src/main/java/io/quarkus/oidc/token/propagation/AccessTokenRequestFilter.java b/extensions/oidc-token-propagation/runtime/src/main/java/io/quarkus/oidc/token/propagation/AccessTokenRequestFilter.java index fa0b8cc77c398..a1fb7787a1e91 100644 --- a/extensions/oidc-token-propagation/runtime/src/main/java/io/quarkus/oidc/token/propagation/AccessTokenRequestFilter.java +++ b/extensions/oidc-token-propagation/runtime/src/main/java/io/quarkus/oidc/token/propagation/AccessTokenRequestFilter.java @@ -15,8 +15,8 @@ import io.quarkus.arc.Arc; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig.Grant; import io.quarkus.oidc.client.OidcClients; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant; import io.quarkus.oidc.common.runtime.OidcConstants; import io.quarkus.oidc.token.propagation.runtime.AbstractTokenRequestFilter; import io.quarkus.runtime.configuration.ConfigurationException; diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java index 252501412a6ff..27ff7831062b6 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java @@ -10,8 +10,8 @@ import java.util.OptionalInt; import io.quarkus.oidc.common.runtime.OidcClientCommonConfig; -import io.quarkus.oidc.common.runtime.OidcCommonConfig; import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.oidc.runtime.OidcConfig; import io.quarkus.security.identity.SecurityIdentityAugmentor; diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java index c818e0d28549e..8acddaf876a62 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java @@ -20,10 +20,10 @@ import io.quarkus.oidc.common.OidcRequestFilter; import io.quarkus.oidc.common.OidcRequestFilter.OidcRequestContext; import io.quarkus.oidc.common.OidcResponseFilter; -import io.quarkus.oidc.common.runtime.OidcClientCommonConfig.Credentials.Secret.Method; import io.quarkus.oidc.common.runtime.OidcClientRedirectException; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.oidc.common.runtime.config.OidcClientCommonConfig.Credentials.Secret.Method; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.groups.UniOnItem; import io.vertx.core.Vertx; @@ -71,7 +71,7 @@ public OidcProviderClient(WebClient client, this.introspectionBasicAuthScheme = initIntrospectionBasicAuthScheme(oidcConfig); this.requestFilters = requestFilters; this.responseFilters = responseFilters; - this.clientSecretQueryAuthentication = oidcConfig.credentials.clientSecret.method.orElse(null) == Method.QUERY; + this.clientSecretQueryAuthentication = oidcConfig.credentials().clientSecret().method().orElse(null) == Method.QUERY; } private static String initIntrospectionBasicAuthScheme(OidcTenantConfig oidcConfig) { @@ -208,23 +208,23 @@ private UniOnItem> getHttpResponse(OidcRequestContextProper request.putHeader(AUTHORIZATION_HEADER, clientSecretBasicAuthScheme); } else if (clientJwtKey != null) { String jwt = OidcCommonUtils.signJwtWithKey(oidcConfig, metadata.getTokenUri(), clientJwtKey); - if (OidcCommonUtils.isClientSecretPostJwtAuthRequired(oidcConfig.credentials)) { + if (OidcCommonUtils.isClientSecretPostJwtAuthRequired(oidcConfig.credentials())) { formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); formBody.add(OidcConstants.CLIENT_SECRET, jwt); } else { formBody.add(OidcConstants.CLIENT_ASSERTION_TYPE, OidcConstants.JWT_BEARER_CLIENT_ASSERTION_TYPE); formBody.add(OidcConstants.CLIENT_ASSERTION, jwt); } - } else if (OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials)) { + } else if (OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials())) { formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); - formBody.add(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials)); + formBody.add(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials())); } else { formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); } buffer = OidcCommonUtils.encodeForm(formBody); } else { formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); - formBody.add(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials)); + formBody.add(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials())); for (Map.Entry entry : formBody) { request.addQueryParam(entry.getKey(), OidcCommonUtils.urlEncode(entry.getValue())); } 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 086148a1eb96f..1072d26ef1b48 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 @@ -41,9 +41,9 @@ import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; import io.quarkus.oidc.common.OidcResponseFilter; -import io.quarkus.oidc.common.runtime.OidcCommonConfig; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcTlsSupport; +import io.quarkus.oidc.common.runtime.config.OidcCommonConfig; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.configuration.ConfigurationException; @@ -539,7 +539,7 @@ protected static Uni createOidcClientUni(OidcTenantConfig oi WebClientOptions options = new WebClientOptions(); options.setFollowRedirects(oidcConfig.followRedirects); - OidcCommonUtils.setHttpClientOptions(oidcConfig, options, tlsSupport.forConfig(oidcConfig.tls)); + OidcCommonUtils.setHttpClientOptions(oidcConfig, options, tlsSupport.forConfig(oidcConfig.tls())); var mutinyVertx = new io.vertx.mutiny.core.Vertx(vertx); WebClient client = WebClient.create(mutinyVertx, options); diff --git a/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcRecorderTest.java b/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcRecorderTest.java index 7c7c2c3a79c03..ac74e246904c8 100644 --- a/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcRecorderTest.java +++ b/extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcRecorderTest.java @@ -6,20 +6,20 @@ import org.junit.jupiter.api.Test; -import io.quarkus.oidc.OidcTenantConfig; +import io.quarkus.oidc.common.runtime.OidcCommonConfig.Proxy; public class OidcRecorderTest { @Test public void testtoProxyOptionsWithHostCheckPresent() { - OidcTenantConfig.Proxy proxy = new OidcTenantConfig.Proxy(); + Proxy proxy = new Proxy(); proxy.host = Optional.of("server.example.com"); assertTrue(OidcRecorder.toProxyOptions(proxy).isPresent()); } @Test public void testtoProxyOptionsWithoutHostCheckNonPresent() { - OidcTenantConfig.Proxy proxy = new OidcTenantConfig.Proxy(); + Proxy proxy = new Proxy(); assertFalse(OidcRecorder.toProxyOptions(proxy).isPresent()); } diff --git a/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientCreator.java b/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientCreator.java index 7e3457dc80fed..502868ed42a08 100644 --- a/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientCreator.java +++ b/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientCreator.java @@ -9,9 +9,9 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import io.quarkus.oidc.client.OidcClient; -import io.quarkus.oidc.client.OidcClientConfig; -import io.quarkus.oidc.client.OidcClientConfig.Grant.Type; import io.quarkus.oidc.client.OidcClients; +import io.quarkus.oidc.client.runtime.OidcClientConfig; +import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant.Type; import io.quarkus.runtime.StartupEvent; import io.smallrye.mutiny.Uni; @@ -40,14 +40,14 @@ public OidcClient getOidcClient() { } private Uni createOidcClient() { - OidcClientConfig cfg = new OidcClientConfig(); - cfg.setId("myclient"); - cfg.setAuthServerUrl(oidcProviderAddress); - cfg.setClientId(oidcClientId); - cfg.getCredentials().setSecret(oidcClientSecret); - cfg.getGrant().setType(Type.PASSWORD); - cfg.setGrantOptions(Map.of("password", - Map.of("username", "jdoe", "password", "jdoe"))); + OidcClientConfig cfg = OidcClientConfig + .authServerUrl(oidcProviderAddress) + .id("myclient") + .clientId(oidcClientId) + .credentials(oidcClientSecret) + .grant(Type.PASSWORD) + .grantOptions("password", Map.of("username", "jdoe", "password", "jdoe")) + .build(); return oidcClients.newClient(cfg); } }