KEYCLOAK-1881 Clone OIDC adapter HttpClient tools to SAML adapter
and KEYCLOAK-1881 Extract httpclient configuration from AdapterConfig
This commit is contained in:
parent
70a8255eae
commit
057cc37b60
10 changed files with 956 additions and 5 deletions
|
@ -38,7 +38,7 @@ import org.apache.http.params.BasicHttpParams;
|
||||||
import org.apache.http.params.HttpConnectionParams;
|
import org.apache.http.params.HttpConnectionParams;
|
||||||
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 org.keycloak.representations.adapters.config.AdapterHttpClientConfig;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
@ -333,7 +333,7 @@ public class HttpClientBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpClient build(AdapterConfig adapterConfig) {
|
public HttpClient build(AdapterHttpClientConfig adapterConfig) {
|
||||||
disableCookieCache(); // disable cookie cache as we don't want sticky sessions for load balancing
|
disableCookieCache(); // disable cookie cache as we don't want sticky sessions for load balancing
|
||||||
|
|
||||||
String truststorePath = adapterConfig.getTruststore();
|
String truststorePath = adapterConfig.getTruststore();
|
||||||
|
@ -379,13 +379,13 @@ public class HttpClientBuilder {
|
||||||
/**
|
/**
|
||||||
* Configures a the proxy to use for auth-server requests if provided.
|
* Configures a the proxy to use for auth-server requests if provided.
|
||||||
* <p>
|
* <p>
|
||||||
* If the given {@link AdapterConfig} contains the attribute {@code proxy-url} we use the
|
* 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.
|
* given URL as a proxy server, otherwise the proxy configuration is ignored.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param adapterConfig
|
* @param adapterConfig
|
||||||
*/
|
*/
|
||||||
private void configureProxyForAuthServerIfProvided(AdapterConfig adapterConfig) {
|
private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) {
|
||||||
|
|
||||||
if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
|
if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.cloned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options relevant for configuring http client that can be used by adapter.
|
||||||
|
*
|
||||||
|
* NOTE: keep in sync with core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java until unified.
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public interface AdapterHttpClientConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns truststore filename.
|
||||||
|
*/
|
||||||
|
public String getTruststore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns truststore password.
|
||||||
|
*/
|
||||||
|
public String getTruststorePassword();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns keystore with client keys.
|
||||||
|
*/
|
||||||
|
public String getClientKeystore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns keystore password.
|
||||||
|
*/
|
||||||
|
public String getClientKeystorePassword();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns boolean flag whether any hostname verification is done on the server's
|
||||||
|
* certificate, {@code true} means that verification is not done.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isAllowAnyHostname();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns boolean flag whether any trust management and hostname verification is done.
|
||||||
|
* <p>
|
||||||
|
* <i>NOTE</i> Disabling trust manager is a security hole, so only set this option
|
||||||
|
* if you cannot or do not want to verify the identity of the
|
||||||
|
* host you are communicating with.
|
||||||
|
*/
|
||||||
|
public boolean isDisableTrustManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns size of connection pool.
|
||||||
|
*/
|
||||||
|
public int getConnectionPoolSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns URL of HTTP proxy.
|
||||||
|
*/
|
||||||
|
public String getProxyUrl();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.cloned;
|
||||||
|
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.keycloak.adapters.saml.descriptor.parsers.SamlDescriptorIDPKeysExtractor;
|
||||||
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:hmlnarik@redhat.com">Hynek Mlnařík</a>
|
||||||
|
*/
|
||||||
|
public class HttpAdapterUtils {
|
||||||
|
|
||||||
|
public static MultivaluedHashMap<String, KeyInfo> downloadKeysFromSamlDescriptor(HttpClient client, String descriptorUrl) throws HttpClientAdapterException {
|
||||||
|
try {
|
||||||
|
HttpGet httpRequest = new HttpGet(descriptorUrl);
|
||||||
|
HttpResponse response = client.execute(httpRequest);
|
||||||
|
int status = response.getStatusLine().getStatusCode();
|
||||||
|
if (status != HttpStatus.SC_OK) {
|
||||||
|
EntityUtils.consumeQuietly(response.getEntity());
|
||||||
|
throw new HttpClientAdapterException("Unexpected status = " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
if (entity == null) {
|
||||||
|
throw new HttpClientAdapterException("There was no entity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
MultivaluedHashMap<String, KeyInfo> res;
|
||||||
|
try (InputStream is = entity.getContent()) {
|
||||||
|
res = extractKeysFromSamlDescriptor(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityUtils.consumeQuietly(entity);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (IOException | ParsingException e) {
|
||||||
|
throw new HttpClientAdapterException("IO error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses SAML descriptor and extracts keys from it.
|
||||||
|
* @param xmlStream
|
||||||
|
* @return List of KeyInfo objects containing keys from the descriptor.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static MultivaluedHashMap<String, KeyInfo> extractKeysFromSamlDescriptor(InputStream xmlStream) throws ParsingException {
|
||||||
|
Object res = new SamlDescriptorIDPKeysExtractor().parse(xmlStream);
|
||||||
|
return (MultivaluedHashMap<String, KeyInfo>) res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.cloned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class HttpClientAdapterException extends Exception {
|
||||||
|
|
||||||
|
public HttpClientAdapterException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientAdapterException(String message, Throwable t) {
|
||||||
|
super(message, t);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,396 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.cloned;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.client.CookieStore;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
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.Scheme;
|
||||||
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
|
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
|
||||||
|
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
|
||||||
|
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||||
|
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||||
|
import org.apache.http.conn.ssl.X509HostnameVerifier;
|
||||||
|
import org.apache.http.cookie.Cookie;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.impl.conn.SingleClientConnManager;
|
||||||
|
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||||
|
import org.apache.http.params.BasicHttpParams;
|
||||||
|
import org.apache.http.params.HttpConnectionParams;
|
||||||
|
import org.keycloak.common.util.EnvUtil;
|
||||||
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraction for creating HttpClients. Allows SSL configuration.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class HttpClientBuilder {
|
||||||
|
public static enum HostnameVerificationPolicy {
|
||||||
|
/**
|
||||||
|
* Hostname verification is not done on the server's certificate
|
||||||
|
*/
|
||||||
|
ANY,
|
||||||
|
/**
|
||||||
|
* Allows wildcards in subdomain names i.e. *.foo.com
|
||||||
|
*/
|
||||||
|
WILDCARD,
|
||||||
|
/**
|
||||||
|
* CN must match hostname connecting to
|
||||||
|
*/
|
||||||
|
STRICT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
private static class PassthroughTrustManager implements X509TrustManager {
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain,
|
||||||
|
String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain,
|
||||||
|
String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected KeyStore truststore;
|
||||||
|
protected KeyStore clientKeyStore;
|
||||||
|
protected String clientPrivateKeyPassword;
|
||||||
|
protected boolean disableTrustManager;
|
||||||
|
protected boolean disableCookieCache = true;
|
||||||
|
protected HostnameVerificationPolicy policy = HostnameVerificationPolicy.WILDCARD;
|
||||||
|
protected SSLContext sslContext;
|
||||||
|
protected int connectionPoolSize = 100;
|
||||||
|
protected int maxPooledPerRoute = 0;
|
||||||
|
protected long connectionTTL = -1;
|
||||||
|
protected TimeUnit connectionTTLUnit = TimeUnit.MILLISECONDS;
|
||||||
|
protected HostnameVerifier verifier = null;
|
||||||
|
protected long socketTimeout = -1;
|
||||||
|
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||||
|
protected long establishConnectionTimeout = -1;
|
||||||
|
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||||
|
protected HttpHost proxyHost;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket inactivity timeout
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* @param unit
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit) {
|
||||||
|
this.socketTimeout = timeout;
|
||||||
|
this.socketTimeoutUnits = unit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When trying to make an initial socket connection, what is the timeout?
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* @param unit
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit) {
|
||||||
|
this.establishConnectionTimeout = timeout;
|
||||||
|
this.establishConnectionTimeoutUnits = unit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder connectionTTL(long ttl, TimeUnit unit) {
|
||||||
|
this.connectionTTL = ttl;
|
||||||
|
this.connectionTTLUnit = unit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder maxPooledPerRoute(int maxPooledPerRoute) {
|
||||||
|
this.maxPooledPerRoute = maxPooledPerRoute;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder connectionPoolSize(int connectionPoolSize) {
|
||||||
|
this.connectionPoolSize = connectionPoolSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable trust management and hostname verification. <i>NOTE</i> this is a security
|
||||||
|
* hole, so only set this option if you cannot or do not want to verify the identity of the
|
||||||
|
* host you are communicating with.
|
||||||
|
*/
|
||||||
|
public HttpClientBuilder disableTrustManager() {
|
||||||
|
this.disableTrustManager = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder disableCookieCache() {
|
||||||
|
this.disableCookieCache = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL policy used to verify hostnames
|
||||||
|
*
|
||||||
|
* @param policy
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpClientBuilder hostnameVerification(HostnameVerificationPolicy policy) {
|
||||||
|
this.policy = policy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public HttpClientBuilder sslContext(SSLContext sslContext) {
|
||||||
|
this.sslContext = sslContext;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder trustStore(KeyStore truststore) {
|
||||||
|
this.truststore = truststore;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder keyStore(KeyStore keyStore, String password) {
|
||||||
|
this.clientKeyStore = keyStore;
|
||||||
|
this.clientPrivateKeyPassword = password;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder keyStore(KeyStore keyStore, char[] password) {
|
||||||
|
this.clientKeyStore = keyStore;
|
||||||
|
this.clientPrivateKeyPassword = new String(password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class VerifierWrapper implements X509HostnameVerifier {
|
||||||
|
protected HostnameVerifier verifier;
|
||||||
|
|
||||||
|
VerifierWrapper(HostnameVerifier verifier) {
|
||||||
|
this.verifier = verifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(String host, SSLSocket ssl) throws IOException {
|
||||||
|
if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(String host, X509Certificate cert) throws SSLException {
|
||||||
|
throw new SSLException("This verification path not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
|
||||||
|
throw new SSLException("This verification path not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String s, SSLSession sslSession) {
|
||||||
|
return verifier.verify(s, sslSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClient build() {
|
||||||
|
X509HostnameVerifier verifier = null;
|
||||||
|
if (this.verifier != null) verifier = new VerifierWrapper(this.verifier);
|
||||||
|
else {
|
||||||
|
switch (policy) {
|
||||||
|
case ANY:
|
||||||
|
verifier = new AllowAllHostnameVerifier();
|
||||||
|
break;
|
||||||
|
case WILDCARD:
|
||||||
|
verifier = new BrowserCompatHostnameVerifier();
|
||||||
|
break;
|
||||||
|
case STRICT:
|
||||||
|
verifier = new StrictHostnameVerifier();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SSLSocketFactory sslsf = null;
|
||||||
|
SSLContext theContext = sslContext;
|
||||||
|
if (disableTrustManager) {
|
||||||
|
theContext = SSLContext.getInstance("SSL");
|
||||||
|
theContext.init(null, new TrustManager[]{new PassthroughTrustManager()},
|
||||||
|
new SecureRandom());
|
||||||
|
verifier = new AllowAllHostnameVerifier();
|
||||||
|
sslsf = new SniSSLSocketFactory(theContext, verifier);
|
||||||
|
} else if (theContext != null) {
|
||||||
|
sslsf = new SniSSLSocketFactory(theContext, verifier);
|
||||||
|
} else if (clientKeyStore != null || truststore != null) {
|
||||||
|
sslsf = new SniSSLSocketFactory(SSLSocketFactory.TLS, clientKeyStore, clientPrivateKeyPassword, truststore, null, verifier);
|
||||||
|
} else {
|
||||||
|
final SSLContext tlsContext = SSLContext.getInstance(SSLSocketFactory.TLS);
|
||||||
|
tlsContext.init(null, null, null);
|
||||||
|
sslsf = new SniSSLSocketFactory(tlsContext, verifier);
|
||||||
|
}
|
||||||
|
SchemeRegistry registry = new SchemeRegistry();
|
||||||
|
registry.register(
|
||||||
|
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||||
|
Scheme httpsScheme = new Scheme("https", 443, sslsf);
|
||||||
|
registry.register(httpsScheme);
|
||||||
|
ClientConnectionManager cm = null;
|
||||||
|
if (connectionPoolSize > 0) {
|
||||||
|
ThreadSafeClientConnManager tcm = new ThreadSafeClientConnManager(registry, connectionTTL, connectionTTLUnit);
|
||||||
|
tcm.setMaxTotal(connectionPoolSize);
|
||||||
|
if (maxPooledPerRoute == 0) maxPooledPerRoute = connectionPoolSize;
|
||||||
|
tcm.setDefaultMaxPerRoute(maxPooledPerRoute);
|
||||||
|
cm = tcm;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
cm = new SingleClientConnManager(registry);
|
||||||
|
}
|
||||||
|
BasicHttpParams params = new BasicHttpParams();
|
||||||
|
|
||||||
|
if (proxyHost != null) {
|
||||||
|
params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socketTimeout > -1) {
|
||||||
|
HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
|
||||||
|
|
||||||
|
}
|
||||||
|
if (establishConnectionTimeout > -1) {
|
||||||
|
HttpConnectionParams.setConnectionTimeout(params, (int) establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
|
||||||
|
}
|
||||||
|
DefaultHttpClient client = new DefaultHttpClient(cm, params);
|
||||||
|
|
||||||
|
if (disableCookieCache) {
|
||||||
|
client.setCookieStore(new CookieStore() {
|
||||||
|
@Override
|
||||||
|
public void addCookie(Cookie cookie) {
|
||||||
|
//To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Cookie> 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 client;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClient build(AdapterHttpClientConfig adapterConfig) {
|
||||||
|
disableCookieCache(); // 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = 10;
|
||||||
|
if (adapterConfig.getConnectionPoolSize() > 0)
|
||||||
|
size = adapterConfig.getConnectionPoolSize();
|
||||||
|
HttpClientBuilder.HostnameVerificationPolicy policy = HttpClientBuilder.HostnameVerificationPolicy.WILDCARD;
|
||||||
|
if (adapterConfig.isAllowAnyHostname())
|
||||||
|
policy = HttpClientBuilder.HostnameVerificationPolicy.ANY;
|
||||||
|
connectionPoolSize(size);
|
||||||
|
hostnameVerification(policy);
|
||||||
|
if (adapterConfig.isDisableTrustManager()) {
|
||||||
|
disableTrustManager();
|
||||||
|
} else {
|
||||||
|
trustStore(truststore);
|
||||||
|
}
|
||||||
|
|
||||||
|
configureProxyForAuthServerIfProvided(adapterConfig);
|
||||||
|
|
||||||
|
return build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a the proxy to use for auth-server requests if provided.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.cloned;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.conn.scheme.HostNameResolver;
|
||||||
|
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||||
|
import org.apache.http.conn.ssl.TrustStrategy;
|
||||||
|
import org.apache.http.conn.ssl.X509HostnameVerifier;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivilegedActionException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSLSocketFactory that uses Server Name Indication (SNI) TLS extension.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Originally copied from <b>keycloak-adapter-core</b> project.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
* @author <a href="mailto:hmlnarik@redhat.com">Hynek Mlnařík</a>
|
||||||
|
*/
|
||||||
|
public class SniSSLSocketFactory extends SSLSocketFactory {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(SniSSLSocketFactory.class.getName());
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(String algorithm, KeyStore keystore, String keyPassword, KeyStore truststore, SecureRandom random, HostNameResolver nameResolver) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(algorithm, keystore, keyPassword, truststore, random, nameResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(String algorithm, KeyStore keystore, String keyPassword, KeyStore truststore, SecureRandom random, TrustStrategy trustStrategy, X509HostnameVerifier hostnameVerifier) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(algorithm, keystore, keyPassword, truststore, random, trustStrategy, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(String algorithm, KeyStore keystore, String keyPassword, KeyStore truststore, SecureRandom random, X509HostnameVerifier hostnameVerifier) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(algorithm, keystore, keyPassword, truststore, random, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(KeyStore keystore, String keystorePassword, KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(keystore, keystorePassword, truststore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(KeyStore keystore, String keystorePassword) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(keystore, keystorePassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(truststore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(TrustStrategy trustStrategy, X509HostnameVerifier hostnameVerifier) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(trustStrategy, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(trustStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(SSLContext sslContext) {
|
||||||
|
super(sslContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(SSLContext sslContext, HostNameResolver nameResolver) {
|
||||||
|
super(sslContext, nameResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) {
|
||||||
|
super(sslContext, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(SSLContext sslContext, String[] supportedProtocols, String[] supportedCipherSuites, X509HostnameVerifier hostnameVerifier) {
|
||||||
|
super(sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory, X509HostnameVerifier hostnameVerifier) {
|
||||||
|
super(socketfactory, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SniSSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory, String[] supportedProtocols, String[] supportedCipherSuites, X509HostnameVerifier hostnameVerifier) {
|
||||||
|
super(socketfactory, supportedProtocols, supportedCipherSuites, hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
|
||||||
|
return super.connectSocket(connectTimeout, applySNI(socket, host.getHostName()), host, remoteAddress, localAddress, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
|
||||||
|
return super.createLayeredSocket(applySNI(socket, target), target, port, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Socket applySNI(final Socket socket, String hostname) {
|
||||||
|
if (socket instanceof SSLSocket) {
|
||||||
|
try {
|
||||||
|
Method setHostMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
|
||||||
|
@Override
|
||||||
|
public Method run() throws NoSuchMethodException {
|
||||||
|
return socket.getClass().getMethod("setHost", String.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setHostMethod.invoke(socket, hostname);
|
||||||
|
LOG.log(Level.FINEST, "Applied SNI to socket for host {0}", hostname);
|
||||||
|
} catch (PrivilegedActionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
|
LOG.log(Level.WARNING, "Failed to apply SNI to SSLSocket", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.keycloak.adapters.cloned;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||||
|
import javax.xml.crypto.dsig.keyinfo.KeyName;
|
||||||
|
import javax.xml.crypto.dsig.keyinfo.X509Data;
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.keycloak.adapters.saml.config.parsers.ConfigXmlConstants;
|
||||||
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||||
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public class HttpAdapterUtilsTest {
|
||||||
|
|
||||||
|
private <T> T getContent(List<Object> objects, Class<T> clazz) {
|
||||||
|
for (Object o : objects) {
|
||||||
|
if (clazz.isInstance(o)) {
|
||||||
|
return (T) o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtractKeysFromSamlDescriptor() throws ParsingException {
|
||||||
|
InputStream xmlStream = HttpAdapterUtilsTest.class.getResourceAsStream("saml-descriptor-valid.xml");
|
||||||
|
MultivaluedHashMap<String, KeyInfo> res = HttpAdapterUtils.extractKeysFromSamlDescriptor(xmlStream);
|
||||||
|
|
||||||
|
assertThat(res, notNullValue());
|
||||||
|
assertThat(res.keySet(), hasItems(KeyTypes.SIGNING.value()));
|
||||||
|
assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR), notNullValue());
|
||||||
|
assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR).size(), equalTo(2));
|
||||||
|
|
||||||
|
KeyInfo ki;
|
||||||
|
KeyName keyName;
|
||||||
|
X509Data x509data;
|
||||||
|
X509Certificate x509certificate;
|
||||||
|
|
||||||
|
ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(0);
|
||||||
|
assertThat(ki.getContent().size(), equalTo(2));
|
||||||
|
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
|
||||||
|
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
|
||||||
|
|
||||||
|
keyName = getContent(ki.getContent(), KeyName.class);
|
||||||
|
assertThat(keyName.getName(), equalTo("rJkJlvowmv1Id74GznieaAC5jU5QQp_ILzuG-GsweTI"));
|
||||||
|
|
||||||
|
x509data = getContent(ki.getContent(), X509Data.class);
|
||||||
|
assertThat(x509data, notNullValue());
|
||||||
|
x509certificate = getContent(x509data.getContent(), X509Certificate.class);
|
||||||
|
assertThat(x509certificate, notNullValue());
|
||||||
|
assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA"));
|
||||||
|
|
||||||
|
ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(1);
|
||||||
|
assertThat(ki.getContent().size(), equalTo(2));
|
||||||
|
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
|
||||||
|
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
|
||||||
|
|
||||||
|
keyName = getContent(ki.getContent(), KeyName.class);
|
||||||
|
assertThat(keyName.getName(), equalTo("BzYc4GwL8HVrAhNyNdp-lTah2DvU9jU03kby9Ynohr4"));
|
||||||
|
|
||||||
|
x509data = getContent(ki.getContent(), X509Data.class);
|
||||||
|
assertThat(x509data, notNullValue());
|
||||||
|
x509certificate = getContent(x509data.getContent(), X509Certificate.class);
|
||||||
|
assertThat(x509certificate, notNullValue());
|
||||||
|
assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
~ and other contributors as indicated by the @author tags.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Name="urn:keycloak">
|
||||||
|
<EntityDescriptor entityID="http://localhost:8081/auth/realms/master">
|
||||||
|
<IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||||
|
<KeyDescriptor use="signing">
|
||||||
|
<dsig:KeyInfo>
|
||||||
|
<dsig:KeyName>rJkJlvowmv1Id74GznieaAC5jU5QQp_ILzuG-GsweTI</dsig:KeyName>
|
||||||
|
<dsig:X509Data>
|
||||||
|
<dsig:X509Certificate>
|
||||||
|
MIICmzCCAYMCBgFX/9ccIDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDI2MDcxMjUwWhcNMjYxMDI2MDgxNDMwWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPjDrM890OoFWLIU5xNT+v8B8EkpOGY1y/9Yi/yQd95uG/p5LaywiPsw+lPy4tSn1pH/2SxNDST2zynKPDd1lYDev43m0sC2FfD2H73q3udQRqSOxW1e8FrTrGDIHxb82UNrCPlu+fH+xYSkigrkOvLvPigTwSIcu8vgs0lk9FqJ81ty3Wj2e9lS7JJGAJ3pC7rp39VLdJSKbfyj/v2RYBeG5Pscncl8cjUOHUq5u19hThjkU2jOBzgIK2JS0bNmzSfH1eBTZMoCQBI1UJ1IbA8tqjQwpOXc+JkPBRU8T/JUQoQlSR6DTcPFvDgH2oGZYFHFfUontZqtz8jrIt2pxBAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK5VgQp1x1FKgabFI6W/iGuy9ZCRoAixOOEGGkDps6dOEFgTQKTy5D/FZts9KuNxhhiD+NvS0d5BKYa5ITPLVPnGucgYkZhz+/+GhxmbjeQr0eJPaY7ZgLfH3tPA6tfdIkA0iE1En1sKEwt6R6DZjh9jtP9laoUoddTvYaFLJpZ2u1Ik94q6ZqX0fS/RKchaBHjhg6MtqCcHt07CBKHh8XNmKPXVSJC/p0MjyXv+qLaNNqyaAvAw6P6DX1hNjzrdkuaaHGXhu6kkezZUVlDWAm9cd1ppqalSK6ggy7yMW1NWTd/NYOPsFU2TS8DDPzRo14s1Qvw4v+TY6yT0NURJPQA=
|
||||||
|
</dsig:X509Certificate>
|
||||||
|
</dsig:X509Data>
|
||||||
|
</dsig:KeyInfo>
|
||||||
|
</KeyDescriptor>
|
||||||
|
<KeyDescriptor use="signing">
|
||||||
|
<dsig:KeyInfo>
|
||||||
|
<dsig:KeyName>BzYc4GwL8HVrAhNyNdp-lTah2DvU9jU03kby9Ynohr4</dsig:KeyName>
|
||||||
|
<dsig:X509Data>
|
||||||
|
<dsig:X509Certificate>
|
||||||
|
MIICmzCCAYMCBgFX/9eK7TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDI2MDcxMzE4WhcNMjYxMDI2MDgxNDU4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCDLT+40/BWzWPSVmpaSaZRs5lBMQ9VP9TCoXkby4PHqxIWRecTPM8fcNkPNPE/tiR2tUIpMXPDzgXNFA/EMoB3V1OEVXPecjKtiZczdR6pi75CBx7PJ2fSXg6xpjhZmHu0k7x591GZdP8Iiu2E6b9QA2p5VXgNgfuP07XzgabnSvIrLG60Imus3u6C2qA/QEuY7EYQWrFooriYLW6B8s3xU8R1a92SLMT8JsfMWXi+1CzAhIbVvdwUwkhVDDhAU6pUek88QQgxodd3FAMksoijCGFN1yrCkovlFhKb3j9AC6Icd9eeJuwYddN/nMeMGEDOeCcAGBACiaUisjUvZDw1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHAHbBI0CRfdw5ZHxHAjgSQvSj41c/4cfwln4Q7X3I5lMBbW3tcgond6Ku9eU46FzG5VpgXIgvEf4u0O9jUnxLlO50+t2SHwQ1RwHdBWQngVSZCRzscq3KrSzx1hx88qLyqcPrr3QtR92fYipDjENxttT/qJtDMrXlwLZEITlHDoneX319USYB9C4zlrCIsQ5XxQTTyCx886Pz15DSVSRxVp61HGk6ROsX/DG5/xwInlzgMZ0r3JWnAjtAaXqUrcwH9FXxco+xkiqKW79bGhWGQI9sXXvQSSNAaENMIUhxtd9uOi1l5e0EkKHE2fHlYyfdUDnFJWwSMXd/NM+hVI4Lw=
|
||||||
|
</dsig:X509Certificate>
|
||||||
|
</dsig:X509Data>
|
||||||
|
</dsig:KeyInfo>
|
||||||
|
</KeyDescriptor>
|
||||||
|
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
|
||||||
|
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
|
||||||
|
<NameIDFormat>
|
||||||
|
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
|
||||||
|
</NameIDFormat>
|
||||||
|
<NameIDFormat>
|
||||||
|
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||||
|
</NameIDFormat>
|
||||||
|
<NameIDFormat>
|
||||||
|
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||||
|
</NameIDFormat>
|
||||||
|
<NameIDFormat>
|
||||||
|
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||||
|
</NameIDFormat>
|
||||||
|
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
|
||||||
|
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
|
||||||
|
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
|
||||||
|
</IDPSSODescriptor>
|
||||||
|
</EntityDescriptor>
|
||||||
|
</EntitiesDescriptor>
|
|
@ -39,7 +39,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||||
"proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests",
|
"proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests",
|
||||||
"policy-enforcer"
|
"policy-enforcer"
|
||||||
})
|
})
|
||||||
public class AdapterConfig extends BaseAdapterConfig {
|
public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClientConfig {
|
||||||
|
|
||||||
@JsonProperty("allow-any-hostname")
|
@JsonProperty("allow-any-hostname")
|
||||||
protected boolean allowAnyHostname;
|
protected boolean allowAnyHostname;
|
||||||
|
@ -82,6 +82,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
@JsonProperty("proxy-url")
|
@JsonProperty("proxy-url")
|
||||||
protected String proxyUrl;
|
protected String proxyUrl;
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isAllowAnyHostname() {
|
public boolean isAllowAnyHostname() {
|
||||||
return allowAnyHostname;
|
return allowAnyHostname;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +91,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.allowAnyHostname = allowAnyHostname;
|
this.allowAnyHostname = allowAnyHostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isDisableTrustManager() {
|
public boolean isDisableTrustManager() {
|
||||||
return disableTrustManager;
|
return disableTrustManager;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.disableTrustManager = disableTrustManager;
|
this.disableTrustManager = disableTrustManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTruststore() {
|
public String getTruststore() {
|
||||||
return truststore;
|
return truststore;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +109,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.truststore = truststore;
|
this.truststore = truststore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTruststorePassword() {
|
public String getTruststorePassword() {
|
||||||
return truststorePassword;
|
return truststorePassword;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +118,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.truststorePassword = truststorePassword;
|
this.truststorePassword = truststorePassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getClientKeystore() {
|
public String getClientKeystore() {
|
||||||
return clientKeystore;
|
return clientKeystore;
|
||||||
}
|
}
|
||||||
|
@ -122,6 +127,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.clientKeystore = clientKeystore;
|
this.clientKeystore = clientKeystore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getClientKeystorePassword() {
|
public String getClientKeystorePassword() {
|
||||||
return clientKeystorePassword;
|
return clientKeystorePassword;
|
||||||
}
|
}
|
||||||
|
@ -138,6 +144,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.clientKeyPassword = clientKeyPassword;
|
this.clientKeyPassword = clientKeyPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getConnectionPoolSize() {
|
public int getConnectionPoolSize() {
|
||||||
return connectionPoolSize;
|
return connectionPoolSize;
|
||||||
}
|
}
|
||||||
|
@ -202,6 +209,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||||
this.policyEnforcerConfig = policyEnforcerConfig;
|
this.policyEnforcerConfig = policyEnforcerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getProxyUrl() {
|
public String getProxyUrl() {
|
||||||
return proxyUrl;
|
return proxyUrl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.representations.adapters.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options relevant for configuring http client that can be used by adapter.
|
||||||
|
*
|
||||||
|
* NOTE: keep in sync with adapters/saml/core/src/main/java/org/keycloak/adapters/AdapterHttpClientConfig.java until unified.
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public interface AdapterHttpClientConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns truststore filename.
|
||||||
|
*/
|
||||||
|
public String getTruststore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns truststore password.
|
||||||
|
*/
|
||||||
|
public String getTruststorePassword();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns keystore with client keys.
|
||||||
|
*/
|
||||||
|
public String getClientKeystore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns keystore password.
|
||||||
|
*/
|
||||||
|
public String getClientKeystorePassword();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns boolean flag whether any hostname verification is done on the server's
|
||||||
|
* certificate, {@code true} means that verification is not done.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isAllowAnyHostname();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns boolean flag whether any trust management and hostname verification is done.
|
||||||
|
* <p>
|
||||||
|
* <i>NOTE</i> Disabling trust manager is a security hole, so only set this option
|
||||||
|
* if you cannot or do not want to verify the identity of the
|
||||||
|
* host you are communicating with.
|
||||||
|
*/
|
||||||
|
public boolean isDisableTrustManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns size of connection pool.
|
||||||
|
*/
|
||||||
|
public int getConnectionPoolSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns URL of HTTP proxy.
|
||||||
|
*/
|
||||||
|
public String getProxyUrl();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue