Merge pull request #1199 from patriot1burke/master
apache http client fixes
This commit is contained in:
commit
761be66362
40 changed files with 893 additions and 204 deletions
36
connections/http-client/pom.xml
Executable file
36
connections/http-client/pom.xml
Executable file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-http-client</artifactId>
|
||||
<name>Keycloak Connections Apache HttpClient</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,139 @@
|
|||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.EntityBuilder;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.util.EnvUtil;
|
||||
import org.keycloak.util.KeystoreUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultHttpClientFactory implements HttpClientFactory {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultHttpClientFactory.class);
|
||||
|
||||
private volatile CloseableHttpClient httpClient;
|
||||
|
||||
@Override
|
||||
public HttpClientProvider create(KeycloakSession session) {
|
||||
return new HttpClientProvider() {
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int postText(String uri, String text) throws IOException {
|
||||
HttpPost request = new HttpPost(uri);
|
||||
request.setEntity(EntityBuilder.create().setText(text).setContentType(ContentType.TEXT_PLAIN).build());
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
try {
|
||||
return response.getStatusLine().getStatusCode();
|
||||
} finally {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream is = entity.getContent();
|
||||
if (is != null) is.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream get(String uri) throws IOException {
|
||||
HttpGet request = new HttpGet(uri);
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity == null) return null;
|
||||
return entity.getContent();
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
long socketTimeout = config.getLong("socket-timeout-millis", -1L);
|
||||
long establishConnectionTimeout = config.getLong("establish-connection-timeout-millis", -1L);
|
||||
int maxPooledPerRoute = config.getInt("max-pooled-per-route", 0);
|
||||
int connectionPoolSize = config.getInt("connection-pool-size", 200);
|
||||
boolean disableTrustManager = config.getBoolean("disable-trust-manager", false);
|
||||
boolean disableCookies = config.getBoolean("disable-cookies", true);
|
||||
String hostnameVerificationPolicy = config.get("hostname-verification-policy", "WILDCARD");
|
||||
HttpClientBuilder.HostnameVerificationPolicy hostnamePolicy = HttpClientBuilder.HostnameVerificationPolicy.valueOf(hostnameVerificationPolicy);
|
||||
String truststore = config.get("truststore");
|
||||
String truststorePassword = config.get("truststore-password");
|
||||
String clientKeystore = config.get("client-keystore");
|
||||
String clientKeystorePassword = config.get("client-keystore-password");
|
||||
String clientPrivateKeyPassword = config.get("client-key-password");
|
||||
|
||||
HttpClientBuilder builder = new HttpClientBuilder();
|
||||
builder.socketTimeout(socketTimeout, TimeUnit.MILLISECONDS)
|
||||
.establishConnectionTimeout(establishConnectionTimeout, TimeUnit.MILLISECONDS)
|
||||
.maxPooledPerRoute(maxPooledPerRoute)
|
||||
.connectionPoolSize(connectionPoolSize)
|
||||
.hostnameVerification(hostnamePolicy)
|
||||
.disableCookies(disableCookies);
|
||||
if (disableTrustManager) builder.disableTrustManager();
|
||||
if (truststore != null) {
|
||||
truststore = EnvUtil.replace(truststore);
|
||||
try {
|
||||
builder.trustStore(KeystoreUtil.loadKeyStore(truststore, truststorePassword));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load truststore", e);
|
||||
}
|
||||
}
|
||||
if (clientKeystore != null) {
|
||||
clientKeystore = EnvUtil.replace(clientKeystore);
|
||||
try {
|
||||
KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
|
||||
builder.keyStore(clientCertKeystore, clientPrivateKeyPassword);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load keystore", e);
|
||||
}
|
||||
}
|
||||
httpClient = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLContexts;
|
||||
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.X509HostnameVerifier;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
|
||||
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.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
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 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 boolean disableCookies = false;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable cookie management.
|
||||
*/
|
||||
public HttpClientBuilder disableCookies(boolean disable) {
|
||||
this.disableTrustManager = disable;
|
||||
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 CloseableHttpClient 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 {
|
||||
SSLConnectionSocketFactory sslsf = null;
|
||||
SSLContext theContext = sslContext;
|
||||
if (disableTrustManager) {
|
||||
theContext = SSLContext.getInstance("TLS");
|
||||
theContext.init(null, new TrustManager[]{new PassthroughTrustManager()},
|
||||
new SecureRandom());
|
||||
verifier = new AllowAllHostnameVerifier();
|
||||
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
|
||||
} else if (theContext != null) {
|
||||
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
|
||||
} else if (clientKeyStore != null || truststore != null) {
|
||||
theContext = createSslContext("TLS", clientKeyStore, clientPrivateKeyPassword, truststore, null);
|
||||
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
|
||||
} else {
|
||||
final SSLContext tlsContext = SSLContext.getInstance("TLS");
|
||||
tlsContext.init(null, null, null);
|
||||
sslsf = new SSLConnectionSocketFactory(tlsContext, verifier);
|
||||
}
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout((int) establishConnectionTimeout)
|
||||
.setSocketTimeout((int) socketTimeout).build();
|
||||
|
||||
org.apache.http.impl.client.HttpClientBuilder builder = HttpClients.custom()
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setSSLSocketFactory(sslsf)
|
||||
.setMaxConnTotal(connectionPoolSize)
|
||||
.setMaxConnPerRoute(maxPooledPerRoute);
|
||||
if (disableCookies) builder.disableCookieManagement();
|
||||
return builder.build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLContext createSslContext(
|
||||
final String algorithm,
|
||||
final KeyStore keystore,
|
||||
final String keyPassword,
|
||||
final KeyStore truststore,
|
||||
final SecureRandom random)
|
||||
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||
return SSLContexts.custom()
|
||||
.useProtocol(algorithm)
|
||||
.setSecureRandom(random)
|
||||
.loadKeyMaterial(keystore, keyPassword != null ? keyPassword.toCharArray() : null)
|
||||
.loadTrustMaterial(truststore)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface HttpClientFactory extends ProviderFactory<HttpClientProvider> {
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface HttpClientProvider extends Provider {
|
||||
HttpClient getHttpClient();
|
||||
|
||||
/**
|
||||
* Helper method
|
||||
*
|
||||
* @param uri
|
||||
* @param text
|
||||
* @return http response status
|
||||
* @throws IOException
|
||||
*/
|
||||
public int postText(String uri, String text) throws IOException;
|
||||
|
||||
/**
|
||||
* Helper method
|
||||
*
|
||||
* @param uri
|
||||
* @return response stream, you must close this stream or leaks will happen
|
||||
* @throws IOException
|
||||
*/
|
||||
public InputStream get(String uri) throws IOException;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class HttpClientSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsHttpClient";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return HttpClientProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return HttpClientFactory.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.httpclient.DefaultHttpClientFactory
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.httpclient.HttpClientSpi
|
|
@ -19,6 +19,7 @@
|
|||
<module>mongo</module>
|
||||
<module>file</module>
|
||||
<module>mongo-update</module>
|
||||
<module>http-client</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.adapters;
|
||||
package org.keycloak.util;
|
||||
|
||||
import org.keycloak.constants.GenericConstants;
|
||||
|
5
dependencies/server-min/pom.xml
vendored
5
dependencies/server-min/pom.xml
vendored
|
@ -135,6 +135,11 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-single-file</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-http-client</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -169,6 +169,10 @@
|
|||
|
||||
<!-- server all dependencies -->
|
||||
|
||||
<module-def name="org.keycloak.keycloak-connections-http-client">
|
||||
<maven-resource group="org.keycloak" artifact="keycloak-connections-http-client"/>
|
||||
</module-def>
|
||||
|
||||
<module-def name="org.keycloak.keycloak-connections-jpa">
|
||||
<maven-resource group="org.keycloak" artifact="keycloak-connections-jpa"/>
|
||||
</module-def>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-http-client">
|
||||
<resources>
|
||||
<!-- Insert resources here -->
|
||||
</resources>
|
||||
<exports>
|
||||
<include path="META-INF/**"/>
|
||||
</exports>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-model-api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="javax.api"/>
|
||||
<module name="org.apache.httpcomponents" slot="4.3" />
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -21,6 +21,7 @@
|
|||
<module name="javax.ws.rs.api"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
<module name="org.jboss.resteasy.resteasy-multipart-provider"/>
|
||||
<module name="org.keycloak.keycloak-connections-http-client" services="import"/>
|
||||
|
||||
<module name="javax.api"/>
|
||||
</dependencies>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<module name="org.keycloak.keycloak-broker-saml" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-infinispan" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-jpa" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-http-client" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-jpa-liquibase" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo-update" services="import"/>
|
||||
|
|
|
@ -63,6 +63,12 @@
|
|||
"interval": 900
|
||||
},
|
||||
|
||||
"connectionsHttpClient": {
|
||||
"default": {
|
||||
"disable-trust-manager": true
|
||||
}
|
||||
},
|
||||
|
||||
"connectionsJpa": {
|
||||
"default": {
|
||||
"dataSource": "java:jboss/datasources/KeycloakDS",
|
||||
|
|
|
@ -395,6 +395,150 @@ All configuration options are optional. Default value for directory is <literal>
|
|||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Outgoing Server HTTP Requests</title>
|
||||
<para>
|
||||
Keycloak server needs to invoke on remote HTTP endpoints to do things like backchannel logouts and other
|
||||
management functions. Keycloak maintains a HTTP client connection pool which has various configuration
|
||||
settings you can specify before boot time. This is configured in the
|
||||
<literal>standalone/configuration/keycloak-server.json</literal>.
|
||||
By default the setting is like this:
|
||||
<programlisting><![CDATA[
|
||||
"connectionsHttpClient": {
|
||||
"default": {
|
||||
"disable-trust-manager": true
|
||||
}
|
||||
},
|
||||
]]></programlisting>
|
||||
Possible configuration options are:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>establish-connection-timeout-millis</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Timeout for establishing a socket connection.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>socket-timeout-millis</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If an outgoing request does not receive data for this amount of time, timeout the connection.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>connection-pool-size</term>
|
||||
<listitem>
|
||||
<para>
|
||||
How many connections can be in the pool.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>max-pooled-per-route</term>
|
||||
<listitem>
|
||||
<para>
|
||||
How many connections can be pooled per host.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>disable-trust-manager</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If true, HTTPS server certificates are not verified. If you set this to false, you must
|
||||
configure a truststore.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>disable-cookies</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>true</literal> by default. When set to true, this will disable any cookie
|
||||
caching.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>hostname-verification-policy</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>WILDCARD</literal> by default. For HTTPS requests, this verifies the hostname
|
||||
of the server's certificate. <literal>ANY</literal> means that the hostname is not verified.
|
||||
<literal>WILDCARD</literal> Allows wildcards in subdomain names i.e. *.foo.com.
|
||||
<literal>STRICT</literal> CN must match hostname exactly.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>truststore</term>
|
||||
<listitem>
|
||||
<para>
|
||||
The value is the file path to a Java keystore file. If
|
||||
you prefix the path with <literal>classpath:</literal>, then the truststore will be obtained
|
||||
from the deployment's classpath instead.
|
||||
HTTPS
|
||||
requests need a way to verify the host of the server they are talking to. This is
|
||||
what the trustore does. The keystore contains one or more trusted
|
||||
host certificates or certificate authorities.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>truststore-password</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Password for the truststore keystore.
|
||||
This is
|
||||
<emphasis>REQUIRED</emphasis>
|
||||
if
|
||||
<literal>truststore</literal>
|
||||
is set.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>client-keystore</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This is the file path to a Java keystore file.
|
||||
This keystore contains client certificate for two-way SSL.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>client-keystore-password</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Password for the client keystore.
|
||||
This is
|
||||
<emphasis>REQUIRED</emphasis>
|
||||
if
|
||||
<literal>client-keystore</literal>
|
||||
is set.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>client-key-password</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Not supported yet, but we will support in future versions.</emphasis>
|
||||
Password for the client's key.
|
||||
This is
|
||||
<emphasis>REQUIRED</emphasis>
|
||||
if
|
||||
<literal>client-keystore</literal>
|
||||
is set.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
</section>
|
||||
<section id="ssl_modes">
|
||||
<title>SSL/HTTPS Requirement/Modes</title>
|
||||
<warning>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-broker-parent</artifactId>
|
||||
<artifactId>keycloak-examples-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-broker-parent</artifactId>
|
||||
<artifactId>keycloak-examples-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-broker-parent</artifactId>
|
||||
<artifactId>keycloak-examples-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-broker-parent</artifactId>
|
||||
<artifactId>keycloak-examples-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
|
1
forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
Normal file → Executable file
1
forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
Normal file → Executable file
|
@ -138,6 +138,7 @@ failedToProcessResponseMessage=Konnte Response nicht verarbeiten.
|
|||
httpsRequiredMessage=HTTPS erforderlich.
|
||||
realmNotEnabledMessage=Realm nicht aktiviert.
|
||||
invalidRequestMessage=Ung\u00FCltiger Request.
|
||||
failedLogout=Logout failed
|
||||
unknownLoginRequesterMessage=Ung\u00FCltiger login requester
|
||||
loginRequesterNotEnabledMessage=Login requester nicht aktiviert.
|
||||
bearerOnlyMessage=Bearer-only Applikationen k\u00F6nne sich nicht via Browser anmelden.
|
||||
|
|
|
@ -140,6 +140,7 @@ failedToProcessResponseMessage=Failed to process response
|
|||
httpsRequiredMessage=HTTPS required
|
||||
realmNotEnabledMessage=Realm not enabled
|
||||
invalidRequestMessage=Invalid Request
|
||||
failedLogout=Logout failed
|
||||
unknownLoginRequesterMessage=Unknown login requester
|
||||
loginRequesterNotEnabledMessage=Login requester not enabled
|
||||
bearerOnlyMessage=Bearer-only applications are not allowed to initiate browser login
|
||||
|
|
|
@ -135,6 +135,7 @@ failedToProcessResponseMessage=Fallimento nell''elaborazione della risposta
|
|||
httpsRequiredMessage=HTTPS richiesto
|
||||
realmNotEnabledMessage=Realm non abilitato
|
||||
invalidRequestMessage=Richiesta non valida
|
||||
failedLogout=Logout failed
|
||||
unknownLoginRequesterMessage=Richiedente di Login non riconosciuto
|
||||
loginRequesterNotEnabledMessage=Richiedente di Login non abilitato
|
||||
bearerOnlyMessage=Alle applicazioni di tipo Bearer-only non e'' consentito di effettuare il login tramite browser
|
||||
|
|
1
forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
Normal file → Executable file
1
forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
Normal file → Executable file
|
@ -135,6 +135,7 @@ failedToProcessResponseMessage=Falha ao processar a resposta
|
|||
httpsRequiredMessage=HTTPS requerido
|
||||
realmNotEnabledMessage=Realm desativado
|
||||
invalidRequestMessage=Pedido inv\u00E1lido
|
||||
failedLogout=Logout failed
|
||||
unknownLoginRequesterMessage=Solicitante de login desconhecido
|
||||
loginRequesterNotEnabledMessage=Solicitante de login desativado
|
||||
bearerOnlyMessage=Aplica\u00E7\u00F5es somente ao portador n\u00E3o tem permiss\u00E3o para iniciar o login pelo navegador
|
||||
|
|
2
integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
Normal file → Executable file
2
integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
Normal file → Executable file
|
@ -24,7 +24,7 @@ import org.keycloak.RSATokenVerifier;
|
|||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.AdapterUtils;
|
||||
import org.keycloak.adapters.FindFile;
|
||||
import org.keycloak.util.FindFile;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
<artifactId>wildfly-controller</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -557,6 +557,11 @@
|
|||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-http-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo-update</artifactId>
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.keycloak.proxy;
|
|||
import io.undertow.Undertow;
|
||||
import io.undertow.security.api.AuthenticationMechanism;
|
||||
import io.undertow.security.api.AuthenticationMode;
|
||||
import io.undertow.security.handlers.AuthenticationCallHandler;
|
||||
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
|
||||
import io.undertow.security.handlers.SecurityInitialHandler;
|
||||
import io.undertow.security.idm.Account;
|
||||
|
@ -24,7 +23,7 @@ import org.codehaus.jackson.map.ObjectMapper;
|
|||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.FindFile;
|
||||
import org.keycloak.util.FindFile;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
|
@ -35,7 +34,6 @@ import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
|||
import org.keycloak.enums.SslRequired;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.CertificateUtils;
|
||||
import org.keycloak.util.PemUtils;
|
||||
import org.keycloak.util.SystemPropertiesJsonParserFactory;
|
||||
import org.xnio.Option;
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-http-client</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package org.keycloak.protocol.saml;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.client.ClientRequest;
|
||||
import org.jboss.resteasy.client.ClientResponse;
|
||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||
import org.keycloak.connections.httpclient.HttpClientProvider;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
|
@ -37,7 +42,9 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -464,6 +471,11 @@ public class SamlProtocol implements LoginProtocol {
|
|||
public Response finishLogout(UserSessionModel userSession) {
|
||||
logger.debug("finishLogout");
|
||||
String logoutBindingUri = userSession.getNote(SAML_LOGOUT_BINDING_URI);
|
||||
if (logoutBindingUri == null) {
|
||||
logger.error("Can't finish SAML logout as there is no logout binding set");
|
||||
return ErrorPage.error(session, Messages.FAILED_LOGOUT);
|
||||
|
||||
}
|
||||
String logoutRelayState = userSession.getNote(SAML_LOGOUT_RELAY_STATE);
|
||||
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
|
||||
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
|
||||
|
@ -515,35 +527,38 @@ public class SamlProtocol implements LoginProtocol {
|
|||
}
|
||||
|
||||
|
||||
ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor();
|
||||
|
||||
|
||||
try {
|
||||
ClientRequest request = executor.createRequest(logoutUrl);
|
||||
request.formParameter(GeneralConstants.SAML_REQUEST_KEY, logoutRequestString);
|
||||
request.formParameter("BACK_CHANNEL_LOGOUT", "BACK_CHANNEL_LOGOUT"); // for Picketlink adapter, todo remove this
|
||||
ClientResponse response = null;
|
||||
HttpClient httpClient = session.getProvider(HttpClientProvider.class).getHttpClient();
|
||||
for (int i = 0; i < 2; i++) { // follow redirects once
|
||||
try {
|
||||
response = request.post();
|
||||
response.releaseConnection();
|
||||
// Undertow will redirect root urls not ending in "/" to root url + "/". Test for this weird behavior
|
||||
if (response.getStatus() == 302 && !logoutUrl.endsWith("/")) {
|
||||
String redirect = (String)response.getHeaders().getFirst(HttpHeaders.LOCATION);
|
||||
String withSlash = logoutUrl + "/";
|
||||
if (withSlash.equals(redirect)) {
|
||||
request = executor.createRequest(withSlash);
|
||||
request.formParameter(GeneralConstants.SAML_REQUEST_KEY, logoutRequestString);
|
||||
request.formParameter("BACK_CHANNEL_LOGOUT", "BACK_CHANNEL_LOGOUT"); // for Picketlink adapter, todo remove this
|
||||
response = request.post();
|
||||
response.releaseConnection();
|
||||
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(GeneralConstants.SAML_REQUEST_KEY, logoutRequestString));
|
||||
formparams.add(new BasicNameValuePair("BACK_CHANNEL_LOGOUT", "BACK_CHANNEL_LOGOUT")); // for Picketlink todo remove this
|
||||
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
HttpPost post = new HttpPost(logoutUrl);
|
||||
post.setEntity(form);
|
||||
HttpResponse response = httpClient.execute(post);
|
||||
try {
|
||||
int status = response.getStatusLine().getStatusCode();
|
||||
if (status == 302 && !logoutUrl.endsWith("/")) {
|
||||
String redirect = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
|
||||
String withSlash = logoutUrl + "/";
|
||||
if (withSlash.equals(redirect)) {
|
||||
logoutUrl = withSlash;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream is = entity.getContent();
|
||||
if (is != null) is.close();
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
logger.warn("failed to send saml logout", e);
|
||||
}
|
||||
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
<artifactId>keycloak-core-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-http-client</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-forms-common-freemarker</artifactId>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
package org.keycloak.protocol.oidc;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
|
@ -166,14 +165,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
|||
public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
if (!(clientSession.getClient() instanceof ClientModel)) return;
|
||||
ClientModel app = clientSession.getClient();
|
||||
// TODO: Probably non-effective to build executor every time from scratch. Should be likely shared for whole OIDCLoginProtocolFactory
|
||||
ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor();
|
||||
|
||||
try {
|
||||
new ResourceAdminManager().logoutClientSession(uriInfo.getRequestUri(), realm, app, clientSession, executor);
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
new ResourceAdminManager(session).logoutClientSession(uriInfo.getRequestUri(), realm, app, clientSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package org.keycloak.services.managers;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.client.ClientRequest;
|
||||
import org.jboss.resteasy.client.ClientResponse;
|
||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||
import org.keycloak.TokenIdGenerator;
|
||||
import org.keycloak.connections.httpclient.HttpClientProvider;
|
||||
import org.keycloak.constants.AdapterConstants;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
|
@ -18,15 +15,14 @@ import org.keycloak.representations.adapters.action.GlobalRequestResult;
|
|||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||
import org.keycloak.representations.adapters.action.TestAvailabilityAction;
|
||||
import org.keycloak.services.util.HttpClientBuilder;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
import org.keycloak.util.MultivaluedHashMap;
|
||||
import org.keycloak.util.StringPropertyReplacer;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -44,11 +40,10 @@ public class ResourceAdminManager {
|
|||
protected static Logger logger = Logger.getLogger(ResourceAdminManager.class);
|
||||
private static final String CLIENT_SESSION_HOST_PROPERTY = "${application.session.host}";
|
||||
|
||||
public static ApacheHttpClient4Executor createExecutor() {
|
||||
HttpClient client = new HttpClientBuilder()
|
||||
.disableTrustManager() // todo fix this, should have a trust manager or a good default
|
||||
.build();
|
||||
return new ApacheHttpClient4Executor(client);
|
||||
private KeycloakSession session;
|
||||
|
||||
public ResourceAdminManager(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public static String resolveUri(URI requestUri, String uri) {
|
||||
|
@ -101,23 +96,17 @@ public class ResourceAdminManager {
|
|||
}
|
||||
|
||||
protected void logoutUserSessions(URI requestUri, RealmModel realm, List<UserSessionModel> userSessions) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
// Map from "app" to clientSessions for this app
|
||||
MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ClientModel, ClientSessionModel>();
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
putClientSessions(clientSessions, userSession);
|
||||
}
|
||||
|
||||
try {
|
||||
// Map from "app" to clientSessions for this app
|
||||
MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ClientModel, ClientSessionModel>();
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
putClientSessions(clientSessions, userSession);
|
||||
}
|
||||
logger.debugv("logging out {0} resources ", clientSessions.size());
|
||||
//logger.infov("logging out resources: {0}", clientSessions);
|
||||
|
||||
logger.debugv("logging out {0} resources ", clientSessions.size());
|
||||
//logger.infov("logging out resources: {0}", clientSessions);
|
||||
|
||||
for (Map.Entry<ClientModel, List<ClientSessionModel>> entry : clientSessions.entrySet()) {
|
||||
logoutClientSessions(requestUri, realm, entry.getKey(), entry.getValue(), executor);
|
||||
}
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
for (Map.Entry<ClientModel, List<ClientSessionModel>> entry : clientSessions.entrySet()) {
|
||||
logoutClientSessions(requestUri, realm, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,32 +117,25 @@ public class ResourceAdminManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void logoutUserFromClient(URI requestUri, RealmModel realm, ClientModel resource, UserModel user, KeycloakSession session) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
|
||||
List<ClientSessionModel> ourAppClientSessions = null;
|
||||
if (userSessions != null) {
|
||||
MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ClientModel, ClientSessionModel>();
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
putClientSessions(clientSessions, userSession);
|
||||
}
|
||||
ourAppClientSessions = clientSessions.get(resource);
|
||||
public void logoutUserFromClient(URI requestUri, RealmModel realm, ClientModel resource, UserModel user) {
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
|
||||
List<ClientSessionModel> ourAppClientSessions = null;
|
||||
if (userSessions != null) {
|
||||
MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ClientModel, ClientSessionModel>();
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
putClientSessions(clientSessions, userSession);
|
||||
}
|
||||
|
||||
logoutClientSessions(requestUri, realm, resource, ourAppClientSessions, executor);
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
ourAppClientSessions = clientSessions.get(resource);
|
||||
}
|
||||
|
||||
logoutClientSessions(requestUri, realm, resource, ourAppClientSessions);
|
||||
}
|
||||
|
||||
public boolean logoutClientSession(URI requestUri, RealmModel realm, ClientModel resource, ClientSessionModel clientSession, ApacheHttpClient4Executor client) {
|
||||
return logoutClientSessions(requestUri, realm, resource, Arrays.asList(clientSession), client);
|
||||
public boolean logoutClientSession(URI requestUri, RealmModel realm, ClientModel resource, ClientSessionModel clientSession) {
|
||||
return logoutClientSessions(requestUri, realm, resource, Arrays.asList(clientSession));
|
||||
}
|
||||
|
||||
protected boolean logoutClientSessions(URI requestUri, RealmModel realm, ClientModel resource, List<ClientSessionModel> clientSessions, ApacheHttpClient4Executor client) {
|
||||
protected boolean logoutClientSessions(URI requestUri, RealmModel realm, ClientModel resource, List<ClientSessionModel> clientSessions) {
|
||||
String managementUrl = getManagementUrl(requestUri, resource);
|
||||
if (managementUrl != null) {
|
||||
|
||||
|
@ -184,7 +166,7 @@ public class ResourceAdminManager {
|
|||
String host = entry.getKey();
|
||||
List<String> sessionIds = entry.getValue();
|
||||
String currentHostMgmtUrl = managementUrl.replace(CLIENT_SESSION_HOST_PROPERTY, host);
|
||||
allPassed = sendLogoutRequest(realm, resource, sessionIds, userSessions, client, 0, currentHostMgmtUrl) && allPassed;
|
||||
allPassed = sendLogoutRequest(realm, resource, sessionIds, userSessions, 0, currentHostMgmtUrl) && allPassed;
|
||||
}
|
||||
|
||||
return allPassed;
|
||||
|
@ -195,7 +177,7 @@ public class ResourceAdminManager {
|
|||
allSessionIds.addAll(currentIds);
|
||||
}
|
||||
|
||||
return sendLogoutRequest(realm, resource, allSessionIds, userSessions, client, 0, managementUrl);
|
||||
return sendLogoutRequest(realm, resource, allSessionIds, userSessions, 0, managementUrl);
|
||||
}
|
||||
} else {
|
||||
logger.debugv("Can't logout {0}: no management url", resource.getClientId());
|
||||
|
@ -206,36 +188,25 @@ public class ResourceAdminManager {
|
|||
// Methods for logout all
|
||||
|
||||
public GlobalRequestResult logoutAll(URI requestUri, RealmModel realm) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
realm.setNotBefore(Time.currentTime());
|
||||
List<ClientModel> resources = realm.getClients();
|
||||
logger.debugv("logging out {0} resources ", resources.size());
|
||||
|
||||
try {
|
||||
realm.setNotBefore(Time.currentTime());
|
||||
List<ClientModel> resources = realm.getClients();
|
||||
logger.debugv("logging out {0} resources ", resources.size());
|
||||
|
||||
GlobalRequestResult finalResult = new GlobalRequestResult();
|
||||
for (ClientModel resource : resources) {
|
||||
GlobalRequestResult currentResult = logoutClient(requestUri, realm, resource, executor, realm.getNotBefore());
|
||||
finalResult.addAll(currentResult);
|
||||
}
|
||||
return finalResult;
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
GlobalRequestResult finalResult = new GlobalRequestResult();
|
||||
for (ClientModel resource : resources) {
|
||||
GlobalRequestResult currentResult = logoutClient(requestUri, realm, resource, realm.getNotBefore());
|
||||
finalResult.addAll(currentResult);
|
||||
}
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
public GlobalRequestResult logoutClient(URI requestUri, RealmModel realm, ClientModel resource) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
try {
|
||||
resource.setNotBefore(Time.currentTime());
|
||||
return logoutClient(requestUri, realm, resource, executor, resource.getNotBefore());
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
resource.setNotBefore(Time.currentTime());
|
||||
return logoutClient(requestUri, realm, resource, resource.getNotBefore());
|
||||
}
|
||||
|
||||
|
||||
protected GlobalRequestResult logoutClient(URI requestUri, RealmModel realm, ClientModel resource, ApacheHttpClient4Executor executor, int notBefore) {
|
||||
protected GlobalRequestResult logoutClient(URI requestUri, RealmModel realm, ClientModel resource, int notBefore) {
|
||||
List<String> mgmtUrls = getAllManagementUrls(requestUri, resource);
|
||||
if (mgmtUrls.isEmpty()) {
|
||||
logger.debug("No management URL or no registered cluster nodes for the client " + resource.getClientId());
|
||||
|
@ -247,7 +218,7 @@ public class ResourceAdminManager {
|
|||
// Propagate this to all hosts
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (sendLogoutRequest(realm, resource, null, null, executor, notBefore, mgmtUrl)) {
|
||||
if (sendLogoutRequest(realm, resource, null, null, notBefore, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
|
@ -256,54 +227,37 @@ public class ResourceAdminManager {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected boolean sendLogoutRequest(RealmModel realm, ClientModel resource, List<String> adapterSessionIds, List<String> userSessions, ApacheHttpClient4Executor client, int notBefore, String managementUrl) {
|
||||
protected boolean sendLogoutRequest(RealmModel realm, ClientModel resource, List<String> adapterSessionIds, List<String> userSessions, int notBefore, String managementUrl) {
|
||||
LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getClientId(), adapterSessionIds, notBefore, userSessions);
|
||||
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||
if (logger.isDebugEnabled()) logger.debugv("logout resource {0} url: {1} sessionIds: " + adapterSessionIds, resource.getClientId(), managementUrl);
|
||||
ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_LOGOUT).build().toString());
|
||||
ClientResponse response;
|
||||
URI target = UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_LOGOUT).build();
|
||||
try {
|
||||
response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Logout for client '" + resource.getClientId() + "' failed", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
boolean success = response.getStatus() == 204 || response.getStatus() == 200;
|
||||
int status = session.getProvider(HttpClientProvider.class).postText(target.toString(), token);
|
||||
boolean success = status == 204 || status == 200;
|
||||
logger.debugf("logout success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} finally {
|
||||
response.releaseConnection();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Logout for client '" + resource.getClientId() + "' failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalRequestResult pushRealmRevocationPolicy(URI requestUri, RealmModel realm) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
GlobalRequestResult finalResult = new GlobalRequestResult();
|
||||
for (ClientModel client : realm.getClients()) {
|
||||
GlobalRequestResult currentResult = pushRevocationPolicy(requestUri, realm, client, realm.getNotBefore(), executor);
|
||||
finalResult.addAll(currentResult);
|
||||
}
|
||||
return finalResult;
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
GlobalRequestResult finalResult = new GlobalRequestResult();
|
||||
for (ClientModel client : realm.getClients()) {
|
||||
GlobalRequestResult currentResult = pushRevocationPolicy(requestUri, realm, client, realm.getNotBefore());
|
||||
finalResult.addAll(currentResult);
|
||||
}
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
public GlobalRequestResult pushClientRevocationPolicy(URI requestUri, RealmModel realm, ClientModel client) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
return pushRevocationPolicy(requestUri, realm, client, client.getNotBefore(), executor);
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
return pushRevocationPolicy(requestUri, realm, client, client.getNotBefore());
|
||||
}
|
||||
|
||||
|
||||
protected GlobalRequestResult pushRevocationPolicy(URI requestUri, RealmModel realm, ClientModel resource, int notBefore, ApacheHttpClient4Executor executor) {
|
||||
protected GlobalRequestResult pushRevocationPolicy(URI requestUri, RealmModel realm, ClientModel resource, int notBefore) {
|
||||
List<String> mgmtUrls = getAllManagementUrls(requestUri, resource);
|
||||
if (mgmtUrls.isEmpty()) {
|
||||
logger.debugf("No management URL or no registered cluster nodes for the client %s", resource.getClientId());
|
||||
|
@ -315,7 +269,7 @@ public class ResourceAdminManager {
|
|||
// Propagate this to all hosts
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (sendPushRevocationPolicyRequest(realm, resource, notBefore, executor, mgmtUrl)) {
|
||||
if (sendPushRevocationPolicyRequest(realm, resource, notBefore, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
|
@ -324,24 +278,19 @@ public class ResourceAdminManager {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected boolean sendPushRevocationPolicyRequest(RealmModel realm, ClientModel resource, int notBefore, ApacheHttpClient4Executor client, String managementUrl) {
|
||||
protected boolean sendPushRevocationPolicyRequest(RealmModel realm, ClientModel resource, int notBefore, String managementUrl) {
|
||||
PushNotBeforeAction adminAction = new PushNotBeforeAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getClientId(), notBefore);
|
||||
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||
logger.infov("pushRevocation resource: {0} url: {1}", resource.getClientId(), managementUrl);
|
||||
ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).build().toString());
|
||||
ClientResponse response;
|
||||
URI target = UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).build();
|
||||
try {
|
||||
response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to send revocation request", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
boolean success = response.getStatus() == 204 || response.getStatus() == 200;
|
||||
int status = session.getProvider(HttpClientProvider.class).postText(target.toString(), token);
|
||||
boolean success = status == 204 || status == 200;
|
||||
logger.debugf("pushRevocation success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} finally {
|
||||
response.releaseConnection();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to send revocation request", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,45 +301,35 @@ public class ResourceAdminManager {
|
|||
return new GlobalRequestResult();
|
||||
}
|
||||
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
if (logger.isDebugEnabled()) logger.debug("Sending test nodes availability: " + mgmtUrls);
|
||||
if (logger.isDebugEnabled()) logger.debug("Sending test nodes availability: " + mgmtUrls);
|
||||
|
||||
// Propagate this to all hosts
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (sendTestNodeAvailabilityRequest(realm, client, executor, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
}
|
||||
// Propagate this to all hosts
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (sendTestNodeAvailabilityRequest(realm, client, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean sendTestNodeAvailabilityRequest(RealmModel realm, ClientModel client, ApacheHttpClient4Executor httpClient, String managementUrl) {
|
||||
protected boolean sendTestNodeAvailabilityRequest(RealmModel realm, ClientModel client, String managementUrl) {
|
||||
TestAvailabilityAction adminAction = new TestAvailabilityAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, client.getClientId());
|
||||
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||
logger.debugv("testNodes availability resource: {0} url: {1}", client.getClientId(), managementUrl);
|
||||
ClientRequest request = httpClient.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_TEST_AVAILABLE).build().toString());
|
||||
ClientResponse response;
|
||||
URI target = UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_TEST_AVAILABLE).build();
|
||||
try {
|
||||
response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
|
||||
} catch (Exception e) {
|
||||
int status = session.getProvider(HttpClientProvider.class).postText(target.toString(), token);
|
||||
boolean success = status == 204 || status == 200;
|
||||
logger.debugf("testAvailability success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} catch (IOException e) {
|
||||
logger.warn("Availability test failed for uri '" + managementUrl + "'", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
boolean success = response.getStatus() == 204 || response.getStatus() == 200;
|
||||
logger.debugf("testAvailability success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} finally {
|
||||
response.releaseConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -173,4 +173,6 @@ public class Messages {
|
|||
public static final String INVALID_PARAMETER = "invalidParameterMessage";
|
||||
|
||||
public static final String IDENTITY_PROVIDER_LOGIN_FAILURE = "identityProviderLoginFailure";
|
||||
|
||||
public static final String FAILED_LOGOUT = "failedLogout";
|
||||
}
|
||||
|
|
|
@ -289,7 +289,7 @@ public class ClientResource {
|
|||
@POST
|
||||
public GlobalRequestResult pushRevocation() {
|
||||
auth.requireManage();
|
||||
return new ResourceAdminManager().pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
|
||||
return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -341,7 +341,7 @@ public class ClientResource {
|
|||
@POST
|
||||
public GlobalRequestResult logoutAll() {
|
||||
auth.requireManage();
|
||||
return new ResourceAdminManager().logoutClient(uriInfo.getRequestUri(), realm, client);
|
||||
return new ResourceAdminManager(session).logoutClient(uriInfo.getRequestUri(), realm, client);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -356,7 +356,7 @@ public class ClientResource {
|
|||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
new ResourceAdminManager().logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user, session);
|
||||
new ResourceAdminManager(session).logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -410,7 +410,7 @@ public class ClientResource {
|
|||
auth.requireManage();
|
||||
logger.debug("Test availability of cluster nodes");
|
||||
|
||||
return new ResourceAdminManager().testNodesAvailability(uriInfo.getRequestUri(), realm, client);
|
||||
return new ResourceAdminManager(session).testNodesAvailability(uriInfo.getRequestUri(), realm, client);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
|
||||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.connections.httpclient.HttpClientProvider;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
|
@ -16,7 +16,6 @@ import org.keycloak.models.utils.ModelToRepresentation;
|
|||
import org.keycloak.models.utils.RepresentationToModel;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.services.managers.ResourceAdminManager;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.social.SocialIdentityProvider;
|
||||
|
||||
|
@ -93,16 +92,18 @@ public class IdentityProvidersResource {
|
|||
|
||||
String providerId = data.get("providerId").toString();
|
||||
String from = data.get("fromUrl").toString();
|
||||
ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor();
|
||||
InputStream inputStream = null;
|
||||
InputStream inputStream = session.getProvider(HttpClientProvider.class).get(from);
|
||||
try {
|
||||
inputStream = executor.createRequest(from).getTarget(InputStream.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||
Map<String, String> config;
|
||||
config = providerFactory.parseConfig(inputStream);
|
||||
return config;
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||
Map<String, String> config = providerFactory.parseConfig(inputStream);
|
||||
return config;
|
||||
}
|
||||
|
||||
@GET
|
||||
|
|
|
@ -254,7 +254,7 @@ public class RealmAdminResource {
|
|||
@POST
|
||||
public GlobalRequestResult pushRevocation() {
|
||||
auth.requireManage();
|
||||
return new ResourceAdminManager().pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
|
||||
return new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,7 +266,7 @@ public class RealmAdminResource {
|
|||
@POST
|
||||
public GlobalRequestResult logoutAll() {
|
||||
session.sessions().removeUserSessions(realm);
|
||||
return new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
|
||||
return new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,7 +364,6 @@ public class RealmAdminResource {
|
|||
* Query events. Returns all events, or will query based on URL query parameters listed here
|
||||
*
|
||||
* @param client app or oauth client name
|
||||
* @param types type type
|
||||
* @param user user id
|
||||
* @param ipAddress
|
||||
* @param firstResult
|
||||
|
|
|
@ -359,7 +359,7 @@ public class AdapterTestStrategy extends ExternalResource {
|
|||
realm = session.realms().getRealmByName("demo");
|
||||
// need to cleanup so other tests don't fail, so invalidate http sessions on remote clients.
|
||||
UserModel user = session.users().getUserByUsername("bburke@redhat.com", realm);
|
||||
new ResourceAdminManager().logoutUser(null, realm, user, session);
|
||||
new ResourceAdminManager(session).logoutUser(null, realm, user, session);
|
||||
realm.setSsoSessionIdleTimeout(originalIdle);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
|
|
@ -67,6 +67,13 @@
|
|||
"interval": 900
|
||||
},
|
||||
|
||||
"connectionsHttpClient": {
|
||||
"default": {
|
||||
"disable-trust-manager": true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
"connectionsJpa": {
|
||||
"default": {
|
||||
"url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test}",
|
||||
|
|
Loading…
Reference in a new issue