getCookies() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean clearExpired(Date date) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void clear() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ });
+
+ }
+ return clientBuilder.build();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public HttpClient build(AdapterHttpClientConfig adapterConfig) {
+ disableCookieCache(true); // disable cookie cache as we don't want sticky sessions for load balancing
+
+ String truststorePath = adapterConfig.getTruststore();
+ if (truststorePath != null) {
+ truststorePath = EnvUtil.replace(truststorePath);
+ String truststorePassword = adapterConfig.getTruststorePassword();
+ try {
+ this.truststore = KeystoreUtil.loadKeyStore(truststorePath, truststorePassword);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to load truststore", e);
+ }
+ }
+ String clientKeystore = adapterConfig.getClientKeystore();
+ if (clientKeystore != null) {
+ clientKeystore = EnvUtil.replace(clientKeystore);
+ String clientKeystorePassword = adapterConfig.getClientKeystorePassword();
+ try {
+ KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
+ keyStore(clientCertKeystore, clientKeystorePassword);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to load keystore", e);
+ }
+ }
+
+ HttpClientBuilder.HostnameVerificationPolicy policy = HttpClientBuilder.HostnameVerificationPolicy.WILDCARD;
+ if (adapterConfig.isAllowAnyHostname())
+ policy = HttpClientBuilder.HostnameVerificationPolicy.ANY;
+ connectionPoolSize(adapterConfig.getConnectionPoolSize());
+ hostnameVerification(policy);
+ if (adapterConfig.isDisableTrustManager()) {
+ disableTrustManager();
+ } else {
+ trustStore(truststore);
+ }
+
+ configureProxyForAuthServerIfProvided(adapterConfig);
+
+ if (socketTimeout == -1 && adapterConfig.getSocketTimeout() > 0) {
+ socketTimeout(adapterConfig.getSocketTimeout(), TimeUnit.MILLISECONDS);
+ }
+
+ if (establishConnectionTimeout == -1 && adapterConfig.getConnectionTimeout() > 0) {
+ establishConnectionTimeout(adapterConfig.getConnectionTimeout(), TimeUnit.MILLISECONDS);
+ }
+
+ if (connectionTTL == -1 && adapterConfig.getConnectionTTL() > 0) {
+ connectionTTL(adapterConfig.getConnectionTTL(), TimeUnit.MILLISECONDS);
+ }
+
+ return build();
+ }
+
+ /**
+ * Configures a the proxy to use for auth-server requests if provided.
+ *
+ * If the given {@link AdapterHttpClientConfig} contains the attribute {@code proxy-url} we use the
+ * given URL as a proxy server, otherwise the proxy configuration is ignored.
+ *
+ *
+ * @param adapterConfig
+ */
+ private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) {
+
+ if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
+ return;
+ }
+
+ URI uri = URI.create(adapterConfig.getProxyUrl());
+ this.proxyHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+ }
+}
diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java
index 20db1a13e572b..abd5fa3db6d43 100644
--- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java
+++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerAuthorizer.java
@@ -12,7 +12,7 @@
import jakarta.inject.Singleton;
import org.keycloak.AuthorizationContext;
-import org.keycloak.adapters.authorization.KeycloakAdapterPolicyEnforcer;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
@@ -59,9 +59,8 @@ public CheckResult apply(RoutingContext routingContext, SecurityIdentity identit
VertxHttpFacade httpFacade = new VertxHttpFacade(routingContext, credential.getToken(), resolver.getReadTimeout());
- KeycloakAdapterPolicyEnforcer adapterPolicyEnforcer = new KeycloakAdapterPolicyEnforcer(
- resolver.getPolicyEnforcer(identity.getAttribute(TENANT_ID_ATTRIBUTE)));
- AuthorizationContext result = adapterPolicyEnforcer.authorize(httpFacade);
+ PolicyEnforcer policyEnforcer = resolver.getPolicyEnforcer(identity.getAttribute(TENANT_ID_ATTRIBUTE));
+ AuthorizationContext result = policyEnforcer.enforce(httpFacade, httpFacade);
if (result.isGranted()) {
SecurityIdentity newIdentity = enhanceSecurityIdentity(identity, result);
@@ -75,7 +74,7 @@ public CheckResult apply(RoutingContext routingContext, SecurityIdentity identit
@RequestScoped
public AuthzClient getAuthzClient() {
SecurityIdentity identity = (SecurityIdentity) Arc.container().instance(SecurityIdentity.class).get();
- return resolver.getPolicyEnforcer(identity.getAttribute(TENANT_ID_ATTRIBUTE)).getClient();
+ return resolver.getPolicyEnforcer(identity.getAttribute(TENANT_ID_ATTRIBUTE)).getAuthzClient();
}
private SecurityIdentity enhanceSecurityIdentity(SecurityIdentity current,
diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java
index cb59aa64574b7..7165eb7e39c79 100644
--- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java
+++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java
@@ -7,7 +7,6 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
@@ -103,7 +102,15 @@ private static PolicyEnforcer createPolicyEnforcer(OidcTenantConfig oidcConfig,
adapterConfig.setPolicyEnforcerConfig(enforcerConfig);
- return new PolicyEnforcer(KeycloakDeploymentBuilder.build(adapterConfig), adapterConfig);
+ return PolicyEnforcer.builder()
+ .authServerUrl(adapterConfig.getAuthServerUrl())
+ .realm(adapterConfig.getRealm())
+ .clientId(adapterConfig.getResource())
+ .credentials(adapterConfig.getCredentials())
+ .bearerOnly(adapterConfig.isBearerOnly())
+ .enforcerConfig(enforcerConfig)
+ .httpClient(new HttpClientBuilder().build(adapterConfig))
+ .build();
}
private static Map getCredentials(OidcTenantConfig oidcConfig) {
diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java
index e0a3a6a28c115..21e8bd8b8b0cf 100644
--- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java
+++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java
@@ -1,69 +1,115 @@
package io.quarkus.keycloak.pep.runtime;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.InputStream;
-import java.io.OutputStream;
import java.net.URI;
import java.util.List;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.security.cert.X509Certificate;
-
-import org.keycloak.KeycloakSecurityContext;
-import org.keycloak.adapters.OIDCHttpFacade;
-import org.keycloak.adapters.spi.AuthenticationError;
-import org.keycloak.adapters.spi.LogoutError;
-import org.keycloak.jose.jws.JWSInput;
-import org.keycloak.jose.jws.JWSInputException;
-import org.keycloak.representations.AccessToken;
+import org.keycloak.adapters.authorization.TokenPrincipal;
+import org.keycloak.adapters.authorization.spi.HttpRequest;
+import org.keycloak.adapters.authorization.spi.HttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.quarkus.vertx.http.runtime.VertxInputStream;
-import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.Cookie;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
-import io.vertx.core.http.impl.CookieImpl;
import io.vertx.ext.web.RoutingContext;
-public class VertxHttpFacade implements OIDCHttpFacade {
+public class VertxHttpFacade implements HttpRequest, HttpResponse {
- private final Response response;
private final RoutingContext routingContext;
- private final Request request;
- private final String token;
private final long readTimeout;
+ private final HttpRequest request;
+ private final HttpResponse response;
+ private final TokenPrincipal tokenPrincipal;
public VertxHttpFacade(RoutingContext routingContext, String token, long readTimeout) {
this.routingContext = routingContext;
- this.token = token;
this.readTimeout = readTimeout;
this.request = createRequest(routingContext);
this.response = createResponse(routingContext);
+ tokenPrincipal = new TokenPrincipal() {
+ @Override
+ public String getRawToken() {
+ return token;
+ }
+ };
+ }
+
+ @Override
+ public String getRelativePath() {
+ return request.getRelativePath();
+ }
+
+ @Override
+ public String getMethod() {
+ return request.getMethod();
+ }
+
+ @Override
+ public String getURI() {
+ return request.getURI();
+ }
+
+ @Override
+ public List getHeaders(String name) {
+ return request.getHeaders(name);
+ }
+
+ @Override
+ public String getFirstParam(String name) {
+ return request.getFirstParam(name);
}
@Override
- public Request getRequest() {
- return request;
+ public String getCookieValue(String name) {
+ return request.getCookieValue(name);
}
@Override
- public Response getResponse() {
- return response;
+ public String getRemoteAddr() {
+ return request.getRemoteAddr();
}
@Override
- public X509Certificate[] getCertificateChain() {
- try {
- return routingContext.request().peerCertificateChain();
- } catch (SSLPeerUnverifiedException e) {
- throw new RuntimeException("Failed to fetch certificates from request", e);
- }
+ public boolean isSecure() {
+ return request.isSecure();
}
- private Request createRequest(RoutingContext routingContext) {
+ @Override
+ public String getHeader(String name) {
+ return request.getHeader(name);
+ }
+
+ @Override
+ public InputStream getInputStream(boolean buffered) {
+ return request.getInputStream(buffered);
+ }
+
+ @Override
+ public TokenPrincipal getPrincipal() {
+ return request.getPrincipal();
+ }
+
+ @Override
+ public void sendError(int statusCode) {
+ response.sendError(statusCode);
+ }
+
+ @Override
+ public void sendError(int statusCode, String reason) {
+ response.sendError(statusCode, reason);
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ response.setHeader(name, value);
+ }
+
+ private HttpRequest createRequest(RoutingContext routingContext) {
HttpServerRequest request = routingContext.request();
- return new Request() {
+ return new HttpRequest() {
@Override
public String getMethod() {
return request.method().name();
@@ -90,19 +136,14 @@ public String getFirstParam(String param) {
}
@Override
- public String getQueryParamValue(String param) {
- return request.getParam(param);
- }
-
- @Override
- public Cookie getCookie(String cookieName) {
- io.vertx.core.http.Cookie c = request.getCookie(cookieName);
+ public String getCookieValue(String name) {
+ Cookie cookie = request.getCookie(name);
- if (c == null) {
+ if (cookie == null) {
return null;
}
- return new Cookie(c.getName(), c.getValue(), 1, c.getDomain(), c.getPath());
+ return cookie.getValue();
}
@Override
@@ -115,11 +156,6 @@ public List getHeaders(String name) {
return request.headers().getAll(name);
}
- @Override
- public InputStream getInputStream() {
- return getInputStream(false);
- }
-
@Override
public InputStream getInputStream(boolean buffered) {
try {
@@ -136,69 +172,27 @@ public InputStream getInputStream(boolean buffered) {
}
@Override
- public String getRemoteAddr() {
- return request.remoteAddress().host();
+ public TokenPrincipal getPrincipal() {
+ return tokenPrincipal;
}
@Override
- public void setError(AuthenticationError error) {
- // no-op
- }
-
- @Override
- public void setError(LogoutError error) {
- // no-op
+ public String getRemoteAddr() {
+ return request.remoteAddress().host();
}
};
}
- private Response createResponse(RoutingContext routingContext) {
+ private HttpResponse createResponse(RoutingContext routingContext) {
HttpServerResponse response = routingContext.response();
- return new Response() {
- @Override
- public void setStatus(int status) {
- response.setStatusCode(status);
- }
-
- @Override
- public void addHeader(String name, String value) {
- response.headers().add(name, value);
- }
+ return new HttpResponse() {
@Override
public void setHeader(String name, String value) {
response.headers().set(name, value);
}
- @Override
- public void resetCookie(String name, String path) {
- response.removeCookie(name, true);
- }
-
- @Override
- public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure,
- boolean httpOnly) {
- CookieImpl cookie = new CookieImpl(name, value);
-
- cookie.setPath(path);
- cookie.setDomain(domain);
- cookie.setMaxAge(maxAge);
- cookie.setSecure(secure);
- cookie.setHttpOnly(httpOnly);
-
- response.addCookie(cookie);
- }
-
- @Override
- public OutputStream getOutputStream() {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
-
- response.headersEndHandler(event -> response.write(Buffer.buffer().appendBytes(os.toByteArray())));
-
- return os;
- }
-
@Override
public void sendError(int code) {
response.setStatusCode(code);
@@ -210,20 +204,6 @@ public void sendError(int code, String message) {
response.setStatusCode(code);
response.setStatusMessage(message);
}
-
- @Override
- public void end() {
- response.end();
- }
};
}
-
- @Override
- public KeycloakSecurityContext getSecurityContext() {
- try {
- return new KeycloakSecurityContext(token, new JWSInput(token).readJsonContent(AccessToken.class), null, null);
- } catch (JWSInputException e) {
- throw new RuntimeException("Failed to create access token", e);
- }
- }
}
diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java
index 02b6e63752e16..f61dad35278e3 100644
--- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java
+++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java
@@ -35,7 +35,7 @@ public class DevServicesConfig {
* string.
* Set 'quarkus.keycloak.devservices.keycloak-x-image' to override this check.
*/
- @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:21.0.2")
+ @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:22.0.0")
public String imageName;
/**
diff --git a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java
index 40e2da377d0d0..c962a3c60c718 100644
--- a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java
+++ b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java
@@ -1137,9 +1137,12 @@ public void testAccessAndRefreshTokenInjectionWithoutIndexHtmlAndListenerMultiTa
doTestAccessAndRefreshTokenInjectionWithoutIndexHtmlAndListener(webClient);
- page = loginForm.getInputByName("login").click();
-
- assertTrue(page.getBody().asNormalizedText().contains("You are already logged in"));
+ try {
+ page = loginForm.getInputByName("login").click();
+ } catch (FailingHttpStatusCodeException ex) {
+ assertEquals(400, ex.getStatusCode());
+ assertTrue(ex.getResponse().getContentAsString().contains("You are already logged in"));
+ }
webClient.getCookieManager().clearCookies();
}
}