KEYCLOAK-2054 - Allow to configure proxy for auth-server requests in adapters.
Previously the adapter configuration did not support specifying a proxy for auth-server requests issued via the Apache HTTP Client by Keycloak. This made it very difficult to connect an Application with Keycloak that was required to use a proxy. Introduced new `proxy-url` attribute to the adapter configuration which makes it possible to configure a proxy to be used for auth-server requests. Proxy-Host, Proxy-Port and Proxy-Scheme are taken from the configured proxy URL. Note that proxies that require authentication are currently not supported.
This commit is contained in:
parent
d98cd4235c
commit
bccc5fa7b1
3 changed files with 65 additions and 11 deletions
|
@ -17,9 +17,11 @@
|
||||||
|
|
||||||
package org.keycloak.adapters;
|
package org.keycloak.adapters;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.client.CookieStore;
|
import org.apache.http.client.CookieStore;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.conn.ClientConnectionManager;
|
import org.apache.http.conn.ClientConnectionManager;
|
||||||
|
import org.apache.http.conn.params.ConnRoutePNames;
|
||||||
import org.apache.http.conn.scheme.PlainSocketFactory;
|
import org.apache.http.conn.scheme.PlainSocketFactory;
|
||||||
import org.apache.http.conn.scheme.Scheme;
|
import org.apache.http.conn.scheme.Scheme;
|
||||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
|
@ -34,9 +36,9 @@ import org.apache.http.impl.conn.SingleClientConnManager;
|
||||||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||||
import org.apache.http.params.BasicHttpParams;
|
import org.apache.http.params.BasicHttpParams;
|
||||||
import org.apache.http.params.HttpConnectionParams;
|
import org.apache.http.params.HttpConnectionParams;
|
||||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
|
||||||
import org.keycloak.common.util.EnvUtil;
|
import org.keycloak.common.util.EnvUtil;
|
||||||
import org.keycloak.common.util.KeystoreUtil;
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
@ -46,6 +48,7 @@ import javax.net.ssl.SSLSocket;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
@ -112,6 +115,7 @@ public class HttpClientBuilder {
|
||||||
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
|
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||||
protected long establishConnectionTimeout = -1;
|
protected long establishConnectionTimeout = -1;
|
||||||
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
|
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||||
|
protected HttpHost proxyHost;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,8 +125,7 @@ public class HttpClientBuilder {
|
||||||
* @param unit
|
* @param unit
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit)
|
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit) {
|
||||||
{
|
|
||||||
this.socketTimeout = timeout;
|
this.socketTimeout = timeout;
|
||||||
this.socketTimeoutUnits = unit;
|
this.socketTimeoutUnits = unit;
|
||||||
return this;
|
return this;
|
||||||
|
@ -135,8 +138,7 @@ public class HttpClientBuilder {
|
||||||
* @param unit
|
* @param unit
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit)
|
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit) {
|
||||||
{
|
|
||||||
this.establishConnectionTimeout = timeout;
|
this.establishConnectionTimeout = timeout;
|
||||||
this.establishConnectionTimeoutUnits = unit;
|
this.establishConnectionTimeoutUnits = unit;
|
||||||
return this;
|
return this;
|
||||||
|
@ -287,16 +289,20 @@ public class HttpClientBuilder {
|
||||||
cm = new SingleClientConnManager(registry);
|
cm = new SingleClientConnManager(registry);
|
||||||
}
|
}
|
||||||
BasicHttpParams params = new BasicHttpParams();
|
BasicHttpParams params = new BasicHttpParams();
|
||||||
if (socketTimeout > -1)
|
|
||||||
{
|
if (proxyHost != null) {
|
||||||
|
params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socketTimeout > -1) {
|
||||||
HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
|
HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
|
||||||
|
|
||||||
}
|
}
|
||||||
if (establishConnectionTimeout > -1)
|
if (establishConnectionTimeout > -1) {
|
||||||
{
|
|
||||||
HttpConnectionParams.setConnectionTimeout(params, (int) establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
|
HttpConnectionParams.setConnectionTimeout(params, (int) establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
|
||||||
}
|
}
|
||||||
DefaultHttpClient client = new DefaultHttpClient(cm, params);
|
DefaultHttpClient client = new DefaultHttpClient(cm, params);
|
||||||
|
|
||||||
if (disableCookieCache) {
|
if (disableCookieCache) {
|
||||||
client.setCookieStore(new CookieStore() {
|
client.setCookieStore(new CookieStore() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -364,6 +370,28 @@ public class HttpClientBuilder {
|
||||||
} else {
|
} else {
|
||||||
trustStore(truststore);
|
trustStore(truststore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configureProxyForAuthServerIfProvided(adapterConfig);
|
||||||
|
|
||||||
return build();
|
return build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a the proxy to use for auth-server requests if provided.
|
||||||
|
* <p>
|
||||||
|
* If the given {@link AdapterConfig} contains the attribute {@code proxy-url} we use the
|
||||||
|
* given URL as a proxy server, otherwise the proxy configuration is ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param adapterConfig
|
||||||
|
*/
|
||||||
|
private void configureProxyForAuthServerIfProvided(AdapterConfig 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());
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -35,7 +35,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||||
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
|
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
|
||||||
"client-keystore", "client-keystore-password", "client-key-password",
|
"client-keystore", "client-keystore-password", "client-key-password",
|
||||||
"always-refresh-token",
|
"always-refresh-token",
|
||||||
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute"
|
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute",
|
||||||
|
"proxy-url"
|
||||||
})
|
})
|
||||||
public class AdapterConfig extends BaseAdapterConfig {
|
public class AdapterConfig extends BaseAdapterConfig {
|
||||||
|
|
||||||
|
@ -68,6 +69,12 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
@JsonProperty("turn-off-change-session-id-on-login")
|
@JsonProperty("turn-off-change-session-id-on-login")
|
||||||
protected Boolean turnOffChangeSessionIdOnLogin;
|
protected Boolean turnOffChangeSessionIdOnLogin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Proxy url to use for requests to the auth-server, configurable via the adapter config property {@code proxy-url}.
|
||||||
|
*/
|
||||||
|
@JsonProperty("proxy-url")
|
||||||
|
protected String proxyUrl;
|
||||||
|
|
||||||
public boolean isAllowAnyHostname() {
|
public boolean isAllowAnyHostname() {
|
||||||
return allowAnyHostname;
|
return allowAnyHostname;
|
||||||
}
|
}
|
||||||
|
@ -179,4 +186,12 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
public void setTurnOffChangeSessionIdOnLogin(Boolean turnOffChangeSessionIdOnLogin) {
|
public void setTurnOffChangeSessionIdOnLogin(Boolean turnOffChangeSessionIdOnLogin) {
|
||||||
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
|
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProxyUrl() {
|
||||||
|
return proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyUrl(String proxyUrl) {
|
||||||
|
this.proxyUrl = proxyUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,6 +405,17 @@
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>proxy-url</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Defines the proxy to use for requests sent to the auth-server-url.
|
||||||
|
This is <emphasis>OPTIONAL</emphasis>. Note that only the <emphasis>scheme</emphasis>,
|
||||||
|
<emphasis>host</emphasis> and <emphasis>port</emphasis> of the proxy URL are used.
|
||||||
|
Proxies that require authentication are currently not supported.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
Loading…
Reference in a new issue