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:
Thomas Darimont 2016-03-15 16:47:44 +01:00
parent d98cd4235c
commit bccc5fa7b1
3 changed files with 65 additions and 11 deletions

View file

@ -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());
}
} }

View file

@ -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;
}
} }

View file

@ -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>