mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
merge
This commit is contained in:
commit
cc0eb47814
@ -11,6 +11,12 @@ env:
|
||||
global:
|
||||
- MAVEN_SKIP_RC=true
|
||||
- MAVEN_OPTS="-Xms512m -Xmx2048m"
|
||||
matrix:
|
||||
- TESTS=old
|
||||
- TESTS=group1
|
||||
- TESTS=group2
|
||||
- TESTS=group3
|
||||
- TESTS=adapter
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
@ -22,6 +28,6 @@ install:
|
||||
- travis_wait 60 mvn install -Pdistribution -DskipTests=true -B -V -q
|
||||
|
||||
script:
|
||||
- mvn test -B
|
||||
- ./travis-run-tests.sh $TESTS
|
||||
|
||||
sudo: false
|
||||
|
||||
@ -38,7 +38,7 @@ 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 org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.representations.adapters.config.AdapterHttpClientConfig;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
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
|
||||
|
||||
String truststorePath = adapterConfig.getTruststore();
|
||||
@ -379,13 +379,13 @@ public class HttpClientBuilder {
|
||||
/**
|
||||
* Configures a the proxy to use for auth-server requests if provided.
|
||||
* <p>
|
||||
* If the given {@link AdapterConfig} contains the attribute {@code proxy-url} we use the
|
||||
* 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(AdapterConfig adapterConfig) {
|
||||
private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) {
|
||||
|
||||
if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
|
||||
return;
|
||||
|
||||
@ -127,7 +127,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
||||
InputStream configInputStream = getConfigInputStream(context);
|
||||
KeycloakDeployment kd;
|
||||
if (configInputStream == null) {
|
||||
log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
|
||||
log.warning("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
|
||||
kd = new KeycloakDeployment();
|
||||
} else {
|
||||
kd = KeycloakDeploymentBuilder.build(configInputStream);
|
||||
@ -196,6 +196,8 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
||||
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
if (deployment == null || !deployment.isConfigured()) {
|
||||
//needed for the EAP6/AS7 adapter relying on the tomcat core adapter
|
||||
facade.getResponse().sendError(401);
|
||||
return false;
|
||||
}
|
||||
AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment);
|
||||
|
||||
@ -92,7 +92,7 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
KeycloakSecurityContext ksc = exchange.getAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
|
||||
if (ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
|
||||
if (!deployment.isBearerOnly() && ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
|
||||
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
|
||||
}
|
||||
AdapterTokenStore tokenStore = getTokenStore(exchange, facade, deployment, securityContext);
|
||||
|
||||
0
adapters/saml/core/nbproject/project.properties
Normal file
0
adapters/saml/core/nbproject/project.properties
Normal file
@ -34,6 +34,7 @@
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
@ -70,6 +71,11 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -79,7 +79,9 @@ public abstract class AbstractInitiateLogin implements AuthChallenge {
|
||||
binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
|
||||
}
|
||||
|
||||
binding.signWith(keypair);
|
||||
binding.signWith(null, keypair);
|
||||
// TODO: As part of KEYCLOAK-3810, add KeyID to the SAML document
|
||||
// <related DocumentBuilder>.addExtension(new KeycloakKeySamlExtensionGenerator(<key ID>));
|
||||
binding.signDocument();
|
||||
}
|
||||
return binding;
|
||||
|
||||
@ -23,7 +23,14 @@ import org.keycloak.saml.SignatureAlgorithm;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.adapters.saml.rotation.SamlDescriptorPublicKeyLocator;
|
||||
import org.keycloak.rotation.CompositeKeyLocator;
|
||||
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@ -179,10 +186,15 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||
|
||||
public static class DefaultIDP implements IDP {
|
||||
|
||||
private static final int DEFAULT_CACHE_TTL = 24 * 60 * 60;
|
||||
|
||||
private String entityID;
|
||||
private PublicKey signatureValidationKey;
|
||||
private final CompositeKeyLocator signatureValidationKeyLocator = new CompositeKeyLocator();
|
||||
private SingleSignOnService singleSignOnService;
|
||||
private SingleLogoutService singleLogoutService;
|
||||
private final List<PublicKey> signatureValidationKeys = new LinkedList<>();
|
||||
private int minTimeBetweenDescriptorRequests;
|
||||
private HttpClient client;
|
||||
|
||||
@Override
|
||||
public String getEntityID() {
|
||||
@ -200,16 +212,25 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getSignatureValidationKey() {
|
||||
return signatureValidationKey;
|
||||
public KeyLocator getSignatureValidationKeyLocator() {
|
||||
return this.signatureValidationKeyLocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinTimeBetweenDescriptorRequests() {
|
||||
return minTimeBetweenDescriptorRequests;
|
||||
}
|
||||
|
||||
public void setMinTimeBetweenDescriptorRequests(int minTimeBetweenDescriptorRequests) {
|
||||
this.minTimeBetweenDescriptorRequests = minTimeBetweenDescriptorRequests;
|
||||
}
|
||||
|
||||
public void setEntityID(String entityID) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
|
||||
public void setSignatureValidationKey(PublicKey signatureValidationKey) {
|
||||
this.signatureValidationKey = signatureValidationKey;
|
||||
public void addSignatureValidationKey(PublicKey signatureValidationKey) {
|
||||
this.signatureValidationKeys.add(signatureValidationKey);
|
||||
}
|
||||
|
||||
public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
|
||||
@ -219,6 +240,31 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||
public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
|
||||
this.singleLogoutService = singleLogoutService;
|
||||
}
|
||||
|
||||
public void refreshKeyLocatorConfiguration() {
|
||||
this.signatureValidationKeyLocator.clear();
|
||||
|
||||
// When key is set, use that (and only that), otherwise configure dynamic key locator
|
||||
if (! this.signatureValidationKeys.isEmpty()) {
|
||||
this.signatureValidationKeyLocator.add(new HardcodedKeyLocator(this.signatureValidationKeys));
|
||||
} else if (this.singleSignOnService != null) {
|
||||
String samlDescriptorUrl = singleSignOnService.getRequestBindingUrl() + "/descriptor";
|
||||
HttpClient httpClient = getClient();
|
||||
SamlDescriptorPublicKeyLocator samlDescriptorPublicKeyLocator =
|
||||
new SamlDescriptorPublicKeyLocator(
|
||||
samlDescriptorUrl, this.minTimeBetweenDescriptorRequests, DEFAULT_CACHE_TTL, httpClient);
|
||||
this.signatureValidationKeyLocator.add(samlDescriptorPublicKeyLocator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public void setClient(HttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
}
|
||||
|
||||
private IDP idp;
|
||||
|
||||
@ -22,14 +22,18 @@ import org.keycloak.saml.SignatureAlgorithm;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Set;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
|
||||
/**
|
||||
* Represents SAML deployment configuration.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface SamlDeployment {
|
||||
|
||||
enum Binding {
|
||||
POST,
|
||||
REDIRECT;
|
||||
@ -41,20 +45,68 @@ public interface SamlDeployment {
|
||||
}
|
||||
|
||||
public interface IDP {
|
||||
/**
|
||||
* Returns entity identifier of this IdP.
|
||||
* @return see description.
|
||||
*/
|
||||
String getEntityID();
|
||||
|
||||
/**
|
||||
* Returns Single sign on service configuration for this IdP.
|
||||
* @return see description.
|
||||
*/
|
||||
SingleSignOnService getSingleSignOnService();
|
||||
|
||||
/**
|
||||
* Returns Single logout service configuration for this IdP.
|
||||
* @return see description.
|
||||
*/
|
||||
SingleLogoutService getSingleLogoutService();
|
||||
PublicKey getSignatureValidationKey();
|
||||
|
||||
/**
|
||||
* Returns {@link KeyLocator} looking up public keys used for validation of IdP signatures.
|
||||
* @return see description.
|
||||
*/
|
||||
KeyLocator getSignatureValidationKeyLocator();
|
||||
|
||||
/**
|
||||
* Returns minimum time (in seconds) between issuing requests to IdP SAML descriptor.
|
||||
* Used e.g. by {@link KeyLocator} looking up public keys for validation of IdP signatures
|
||||
* to prevent too frequent requests.
|
||||
*
|
||||
* @return see description.
|
||||
*/
|
||||
int getMinTimeBetweenDescriptorRequests();
|
||||
|
||||
/**
|
||||
* Returns {@link HttpClient} instance that will be used for http communication with this IdP.
|
||||
* @return see description
|
||||
*/
|
||||
HttpClient getClient();
|
||||
|
||||
public interface SingleSignOnService {
|
||||
/**
|
||||
* Returns {@code true} if the requests to IdP need to be signed by SP key.
|
||||
* @return see dscription
|
||||
*/
|
||||
boolean signRequest();
|
||||
/**
|
||||
* Returns {@code true} if the complete response message from IdP should
|
||||
* be checked for valid signature.
|
||||
* @return see dscription
|
||||
*/
|
||||
boolean validateResponseSignature();
|
||||
/**
|
||||
* Returns {@code true} if individual assertions in response from IdP should
|
||||
* be checked for valid signature.
|
||||
* @return see dscription
|
||||
*/
|
||||
boolean validateAssertionSignature();
|
||||
Binding getRequestBinding();
|
||||
Binding getResponseBinding();
|
||||
String getRequestBindingUrl();
|
||||
}
|
||||
|
||||
public interface SingleLogoutService {
|
||||
boolean validateRequestSignature();
|
||||
boolean validateResponseSignature();
|
||||
@ -67,10 +119,19 @@ public interface SamlDeployment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Identity Provider configuration for this SAML deployment.
|
||||
* @return see description.
|
||||
*/
|
||||
public IDP getIDP();
|
||||
|
||||
public boolean isConfigured();
|
||||
SslRequired getSslRequired();
|
||||
|
||||
/**
|
||||
* Returns entity identifier of this SP.
|
||||
* @return see description.
|
||||
*/
|
||||
String getEntityID();
|
||||
String getNameIDPolicyFormat();
|
||||
boolean isForceAuthentication();
|
||||
|
||||
@ -19,6 +19,7 @@ package org.keycloak.adapters.saml.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import org.keycloak.adapters.cloned.AdapterHttpClientConfig;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@ -157,12 +158,97 @@ public class IDP implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public static class HttpClientConfig implements AdapterHttpClientConfig {
|
||||
|
||||
private String truststore;
|
||||
private String truststorePassword;
|
||||
private String clientKeystore;
|
||||
private String clientKeystorePassword;
|
||||
private boolean allowAnyHostname;
|
||||
private boolean disableTrustManager;
|
||||
private int connectionPoolSize;
|
||||
private String proxyUrl;
|
||||
|
||||
@Override
|
||||
public String getTruststore() {
|
||||
return truststore;
|
||||
}
|
||||
|
||||
public void setTruststore(String truststore) {
|
||||
this.truststore = truststore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTruststorePassword() {
|
||||
return truststorePassword;
|
||||
}
|
||||
|
||||
public void setTruststorePassword(String truststorePassword) {
|
||||
this.truststorePassword = truststorePassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientKeystore() {
|
||||
return clientKeystore;
|
||||
}
|
||||
|
||||
public void setClientKeystore(String clientKeystore) {
|
||||
this.clientKeystore = clientKeystore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientKeystorePassword() {
|
||||
return clientKeystorePassword;
|
||||
}
|
||||
|
||||
public void setClientKeystorePassword(String clientKeystorePassword) {
|
||||
this.clientKeystorePassword = clientKeystorePassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowAnyHostname() {
|
||||
return allowAnyHostname;
|
||||
}
|
||||
|
||||
public void setAllowAnyHostname(boolean allowAnyHostname) {
|
||||
this.allowAnyHostname = allowAnyHostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisableTrustManager() {
|
||||
return disableTrustManager;
|
||||
}
|
||||
|
||||
public void setDisableTrustManager(boolean disableTrustManager) {
|
||||
this.disableTrustManager = disableTrustManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConnectionPoolSize() {
|
||||
return connectionPoolSize;
|
||||
}
|
||||
|
||||
public void setConnectionPoolSize(int connectionPoolSize) {
|
||||
this.connectionPoolSize = connectionPoolSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProxyUrl() {
|
||||
return proxyUrl;
|
||||
}
|
||||
|
||||
public void setProxyUrl(String proxyUrl) {
|
||||
this.proxyUrl = proxyUrl;
|
||||
}
|
||||
}
|
||||
|
||||
private String entityID;
|
||||
private String signatureAlgorithm;
|
||||
private String signatureCanonicalizationMethod;
|
||||
private SingleSignOnService singleSignOnService;
|
||||
private SingleLogoutService singleLogoutService;
|
||||
private List<Key> keys;
|
||||
private AdapterHttpClientConfig httpClientConfig = new HttpClientConfig();
|
||||
|
||||
public String getEntityID() {
|
||||
return entityID;
|
||||
@ -212,4 +298,12 @@ public class IDP implements Serializable {
|
||||
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
||||
}
|
||||
|
||||
public AdapterHttpClientConfig getHttpClientConfig() {
|
||||
return httpClientConfig;
|
||||
}
|
||||
|
||||
public void setHttpClientConfig(AdapterHttpClientConfig httpClientConfig) {
|
||||
this.httpClientConfig = httpClientConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -72,4 +72,15 @@ public class ConfigXmlConstants {
|
||||
public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
|
||||
public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
|
||||
public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
|
||||
|
||||
public static final String HTTP_CLIENT_ELEMENT = "HttpClient";
|
||||
public static final String ALLOW_ANY_HOSTNAME_ATTR = "allowAnyHostname";
|
||||
public static final String CLIENT_KEYSTORE_ATTR = "clientKeystore";
|
||||
public static final String CLIENT_KEYSTORE_PASSWORD_ATTR = "clientKeystorePassword";
|
||||
public static final String CONNECTION_POOL_SIZE_ATTR = "connectionPoolSize";
|
||||
public static final String DISABLE_TRUST_MANAGER_ATTR = "disableTrustManager";
|
||||
public static final String PROXY_URL_ATTR = "proxyUrl";
|
||||
public static final String TRUSTSTORE_ATTR = "truststore";
|
||||
public static final String TRUSTSTORE_PASSWORD_ATTR = "truststorePassword";
|
||||
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.keycloak.adapters.cloned.HttpClientBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@ -178,35 +179,39 @@ public class DeploymentBuilder {
|
||||
if (sp.getIdp().getKeys() != null) {
|
||||
for (Key key : sp.getIdp().getKeys()) {
|
||||
if (key.isSigning()) {
|
||||
if (key.getKeystore() != null) {
|
||||
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
||||
Certificate cert = null;
|
||||
try {
|
||||
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
||||
} catch (KeyStoreException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
idp.setSignatureValidationKey(cert.getPublicKey());
|
||||
} else {
|
||||
if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
|
||||
throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
|
||||
}
|
||||
try {
|
||||
PublicKey publicKey = getPublicKeyFromPem(key);
|
||||
idp.setSignatureValidationKey(publicKey);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
processSigningKey(idp, key, resourceLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idp.setClient(new HttpClientBuilder().build(sp.getIdp().getHttpClientConfig()));
|
||||
idp.refreshKeyLocatorConfiguration();
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
protected static PublicKey getPublicKeyFromPem(Key key) throws Exception {
|
||||
private void processSigningKey(DefaultSamlDeployment.DefaultIDP idp, Key key, ResourceLoader resourceLoader) throws RuntimeException {
|
||||
PublicKey publicKey;
|
||||
if (key.getKeystore() != null) {
|
||||
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
||||
Certificate cert = null;
|
||||
try {
|
||||
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
||||
} catch (KeyStoreException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
publicKey = cert.getPublicKey();
|
||||
} else {
|
||||
if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
|
||||
throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
|
||||
}
|
||||
publicKey = getPublicKeyFromPem(key);
|
||||
}
|
||||
|
||||
idp.addSignatureValidationKey(publicKey);
|
||||
}
|
||||
|
||||
protected static PublicKey getPublicKeyFromPem(Key key) {
|
||||
PublicKey publicKey;
|
||||
if (key.getPublicKeyPem() != null) {
|
||||
publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim());
|
||||
|
||||
@ -29,6 +29,10 @@ import javax.xml.stream.events.EndElement;
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import java.util.List;
|
||||
import org.keycloak.adapters.saml.config.IDP.HttpClientConfig;
|
||||
import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getAttributeValue;
|
||||
import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getBooleanAttributeValue;
|
||||
import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getIntegerAttributeValue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@ -41,16 +45,16 @@ public class IDPXmlParser extends AbstractParser {
|
||||
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||
StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
|
||||
IDP idp = new IDP();
|
||||
String entityID = SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
|
||||
String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
|
||||
if (entityID == null) {
|
||||
throw new ParsingException("entityID must be set on IDP");
|
||||
|
||||
}
|
||||
idp.setEntityID(entityID);
|
||||
|
||||
boolean signaturesRequired = SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
|
||||
idp.setSignatureCanonicalizationMethod(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
|
||||
idp.setSignatureAlgorithm(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
|
||||
boolean signaturesRequired = getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
|
||||
idp.setSignatureCanonicalizationMethod(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
|
||||
idp.setSignatureAlgorithm(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
|
||||
while (xmlEventReader.hasNext()) {
|
||||
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
|
||||
if (xmlEvent == null)
|
||||
@ -75,6 +79,10 @@ public class IDPXmlParser extends AbstractParser {
|
||||
IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader, signaturesRequired);
|
||||
idp.setSingleLogoutService(slo);
|
||||
|
||||
} else if (tag.equals(ConfigXmlConstants.HTTP_CLIENT_ELEMENT)) {
|
||||
HttpClientConfig config = parseHttpClientElement(xmlEventReader);
|
||||
idp.setHttpClientConfig(config);
|
||||
|
||||
} else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
|
||||
KeysXmlParser parser = new KeysXmlParser();
|
||||
List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
|
||||
@ -90,29 +98,63 @@ public class IDPXmlParser extends AbstractParser {
|
||||
protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
|
||||
IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
|
||||
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||
slo.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
|
||||
slo.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
|
||||
slo.setValidateRequestSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
|
||||
slo.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
||||
slo.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
||||
slo.setSignResponse(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
|
||||
slo.setPostBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
|
||||
slo.setRedirectBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
|
||||
slo.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
|
||||
slo.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
|
||||
slo.setValidateRequestSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
|
||||
slo.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
||||
slo.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
||||
slo.setSignResponse(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
|
||||
slo.setPostBindingUrl(getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
|
||||
slo.setRedirectBindingUrl(getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
|
||||
return slo;
|
||||
}
|
||||
|
||||
protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
|
||||
IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
|
||||
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||
sso.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
|
||||
sso.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
|
||||
sso.setValidateAssertionSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_ASSERTION_SIGNATURE_ATTR));
|
||||
sso.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
||||
sso.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
||||
sso.setBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
|
||||
sso.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
|
||||
sso.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
|
||||
sso.setValidateAssertionSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_ASSERTION_SIGNATURE_ATTR));
|
||||
sso.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
||||
sso.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
||||
sso.setBindingUrl(getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
|
||||
return sso;
|
||||
}
|
||||
|
||||
private HttpClientConfig parseHttpClientElement(XMLEventReader xmlEventReader) throws ParsingException {
|
||||
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||
StaxParserUtil.validate(startElement, ConfigXmlConstants.HTTP_CLIENT_ELEMENT);
|
||||
HttpClientConfig config = new HttpClientConfig();
|
||||
|
||||
config.setAllowAnyHostname(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false));
|
||||
config.setClientKeystore(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_ATTR));
|
||||
config.setClientKeystorePassword(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_PASSWORD_ATTR));
|
||||
config.setConnectionPoolSize(getIntegerAttributeValue(startElement, ConfigXmlConstants.CONNECTION_POOL_SIZE_ATTR, 0));
|
||||
config.setDisableTrustManager(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false));
|
||||
config.setProxyUrl(getAttributeValue(startElement, ConfigXmlConstants.PROXY_URL_ATTR));
|
||||
config.setTruststore(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_ATTR));
|
||||
config.setTruststorePassword(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_PASSWORD_ATTR));
|
||||
|
||||
while (xmlEventReader.hasNext()) {
|
||||
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
|
||||
if (xmlEvent == null)
|
||||
break;
|
||||
if (xmlEvent instanceof EndElement) {
|
||||
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
|
||||
String endElementName = StaxParserUtil.getEndElementName(endElement);
|
||||
if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT))
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
String tag = StaxParserUtil.getStartElementName(startElement);
|
||||
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(QName qname) {
|
||||
return false;
|
||||
|
||||
@ -48,6 +48,13 @@ public class SPXmlParser extends AbstractParser {
|
||||
return str;
|
||||
}
|
||||
|
||||
public static int getIntegerAttributeValue(StartElement startElement, String tag, int defaultValue) {
|
||||
String result = getAttributeValue(startElement, tag);
|
||||
if (result == null)
|
||||
return defaultValue;
|
||||
return Integer.valueOf(result);
|
||||
}
|
||||
|
||||
public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
|
||||
String result = getAttributeValue(startElement, tag);
|
||||
if (result == null)
|
||||
|
||||
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.saml.descriptor.parsers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.dom.DOMStructure;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.processing.core.util.NamespaceContext;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Goes through the given XML file and extracts names, certificates and keys from the KeyInfo elements.
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class SamlDescriptorIDPKeysExtractor {
|
||||
|
||||
private static final NamespaceContext NS_CONTEXT = new NamespaceContext();
|
||||
static {
|
||||
NS_CONTEXT.addNsUriPair("m", JBossSAMLURIConstants.METADATA_NSURI.get());
|
||||
NS_CONTEXT.addNsUriPair("dsig", JBossSAMLURIConstants.XMLDSIG_NSURI.get());
|
||||
}
|
||||
|
||||
private final KeyInfoFactory kif = KeyInfoFactory.getInstance();
|
||||
|
||||
private final XPathFactory xPathfactory = XPathFactory.newInstance();
|
||||
private final XPath xpath = xPathfactory.newXPath();
|
||||
{
|
||||
xpath.setNamespaceContext(NS_CONTEXT);
|
||||
}
|
||||
|
||||
public MultivaluedHashMap<String, KeyInfo> parse(InputStream stream) throws ParsingException {
|
||||
MultivaluedHashMap<String, KeyInfo> res = new MultivaluedHashMap<>();
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(stream);
|
||||
|
||||
XPathExpression expr = xpath.compile("/m:EntitiesDescriptor/m:EntityDescriptor/m:IDPSSODescriptor/m:KeyDescriptor");
|
||||
NodeList keyDescriptors = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
|
||||
for (int i = 0; i < keyDescriptors.getLength(); i ++) {
|
||||
Node keyDescriptor = keyDescriptors.item(i);
|
||||
Element keyDescriptorEl = (Element) keyDescriptor;
|
||||
KeyInfo ki = processKeyDescriptor(keyDescriptorEl);
|
||||
if (ki != null) {
|
||||
String use = keyDescriptorEl.getAttribute(JBossSAMLConstants.USE.get());
|
||||
res.add(use, ki);
|
||||
}
|
||||
}
|
||||
} catch (SAXException | IOException | ParserConfigurationException | MarshalException | XPathExpressionException e) {
|
||||
throw new ParsingException("Error parsing SAML descriptor", e);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private KeyInfo processKeyDescriptor(Element keyDescriptor) throws MarshalException {
|
||||
NodeList childNodes = keyDescriptor.getElementsByTagNameNS(JBossSAMLURIConstants.XMLDSIG_NSURI.get(), JBossSAMLConstants.KEY_INFO.get());
|
||||
|
||||
if (childNodes.getLength() == 0) {
|
||||
return null;
|
||||
}
|
||||
Node keyInfoNode = childNodes.item(0);
|
||||
return (keyInfoNode == null) ? null : kif.unmarshalKeyInfo(new DOMStructure(keyInfoNode));
|
||||
}
|
||||
|
||||
}
|
||||
@ -64,11 +64,20 @@ import org.w3c.dom.Node;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.keycloak.dom.saml.v2.SAML2Object;
|
||||
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -257,13 +266,44 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
}
|
||||
|
||||
private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) throws VerificationException {
|
||||
KeyLocator signatureValidationKey = deployment.getIDP().getSignatureValidationKeyLocator();
|
||||
if (postBinding) {
|
||||
verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
|
||||
verifyPostBindingSignature(holder.getSamlDocument(), signatureValidationKey);
|
||||
} else {
|
||||
verifyRedirectBindingSignature(deployment.getIDP().getSignatureValidationKey(), paramKey);
|
||||
String keyId = getMessageSigningKeyId(holder.getSamlObject());
|
||||
verifyRedirectBindingSignature(paramKey, signatureValidationKey, keyId);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMessageSigningKeyId(SAML2Object doc) {
|
||||
final ExtensionsType extensions;
|
||||
if (doc instanceof RequestAbstractType) {
|
||||
extensions = ((RequestAbstractType) doc).getExtensions();
|
||||
} else if (doc instanceof StatusResponseType) {
|
||||
extensions = ((StatusResponseType) doc).getExtensions();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (extensions == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Object ext : extensions.getAny()) {
|
||||
if (! (ext instanceof Element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String res = KeycloakKeySamlExtensionGenerator.getMessageSigningKeyIdFromElement((Element) ext);
|
||||
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean checkStatusCodeValue(StatusCodeType statusCode, String expectedValue){
|
||||
if(statusCode != null && statusCode.getValue()!=null){
|
||||
String v = statusCode.getValue().toString();
|
||||
@ -473,10 +513,10 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
return false;
|
||||
}
|
||||
|
||||
public void verifyPostBindingSignature(Document document, PublicKey publicKey) throws VerificationException {
|
||||
public void verifyPostBindingSignature(Document document, KeyLocator keyLocator) throws VerificationException {
|
||||
SAML2Signature saml2Signature = new SAML2Signature();
|
||||
try {
|
||||
if (!saml2Signature.validate(document, publicKey)) {
|
||||
if (!saml2Signature.validate(document, keyLocator)) {
|
||||
throw new VerificationException("Invalid signature on document");
|
||||
}
|
||||
} catch (ProcessingException e) {
|
||||
@ -484,7 +524,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyRedirectBindingSignature(PublicKey publicKey, String paramKey) throws VerificationException {
|
||||
private void verifyRedirectBindingSignature(String paramKey, KeyLocator keyLocator, String keyId) throws VerificationException {
|
||||
String request = facade.getRequest().getQueryParamValue(paramKey);
|
||||
String algorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
||||
String signature = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
|
||||
@ -511,16 +551,80 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
try {
|
||||
//byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
|
||||
byte[] decodedSignature = Base64.decode(signature);
|
||||
byte[] rawQueryBytes = rawQuery.getBytes("UTF-8");
|
||||
|
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
|
||||
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
|
||||
validator.initVerify(publicKey);
|
||||
validator.update(rawQuery.getBytes("UTF-8"));
|
||||
if (!validator.verify(decodedSignature)) {
|
||||
|
||||
if (! validateRedirectBindingSignature(signatureAlgorithm, rawQueryBytes, decodedSignature, keyLocator, keyId)) {
|
||||
throw new VerificationException("Invalid query param signature");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new VerificationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateRedirectBindingSignature(SignatureAlgorithm sigAlg, byte[] rawQueryBytes, byte[] decodedSignature, KeyLocator locator, String keyId)
|
||||
throws KeyManagementException, VerificationException {
|
||||
try {
|
||||
Key key;
|
||||
try {
|
||||
key = locator.getKey(keyId);
|
||||
boolean keyLocated = key != null;
|
||||
|
||||
if (validateRedirectBindingSignatureForKey(sigAlg, rawQueryBytes, decodedSignature, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyLocated) {
|
||||
return false;
|
||||
}
|
||||
} catch (KeyManagementException ex) {
|
||||
}
|
||||
} catch (SignatureException ex) {
|
||||
log.debug("Verification failed for key %s: %s", keyId, ex);
|
||||
log.trace(ex);
|
||||
}
|
||||
|
||||
if (locator instanceof Iterable) {
|
||||
Iterable<Key> availableKeys = (Iterable<Key>) locator;
|
||||
|
||||
log.trace("Trying hard to validate XML signature using all available keys.");
|
||||
|
||||
for (Key key : availableKeys) {
|
||||
try {
|
||||
if (validateRedirectBindingSignatureForKey(sigAlg, rawQueryBytes, decodedSignature, key)) {
|
||||
return true;
|
||||
}
|
||||
} catch (SignatureException ex) {
|
||||
log.debug("Verification failed: %s", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean validateRedirectBindingSignatureForKey(SignatureAlgorithm sigAlg, byte[] rawQueryBytes, byte[] decodedSignature, Key key)
|
||||
throws SignatureException {
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! (key instanceof PublicKey)) {
|
||||
log.warnf("Unusable key for signature validation: %s", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
Signature signature = sigAlg.createSignature(); // todo plugin signature alg
|
||||
try {
|
||||
signature.initVerify((PublicKey) key);
|
||||
} catch (InvalidKeyException ex) {
|
||||
log.warnf(ex, "Unusable key for signature validation: %s", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
signature.update(rawQueryBytes);
|
||||
|
||||
return signature.verify(decodedSignature);
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,8 +82,10 @@ public class WebBrowserSsoAuthenticationHandler extends AbstractSamlAuthenticati
|
||||
if (deployment.getSignatureCanonicalizationMethod() != null)
|
||||
binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
|
||||
binding.signatureAlgorithm(deployment.getSignatureAlgorithm())
|
||||
.signWith(deployment.getSigningKeyPair())
|
||||
.signWith(null, deployment.getSigningKeyPair())
|
||||
.signDocument();
|
||||
// TODO: As part of KEYCLOAK-3810, add KeyID to the SAML document
|
||||
// <related DocumentBuilder>.addExtension(new KeycloakKeySamlExtensionGenerator(<key ID>));
|
||||
}
|
||||
|
||||
|
||||
@ -113,8 +115,10 @@ public class WebBrowserSsoAuthenticationHandler extends AbstractSamlAuthenticati
|
||||
if (deployment.getSignatureCanonicalizationMethod() != null)
|
||||
binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
|
||||
binding.signatureAlgorithm(deployment.getSignatureAlgorithm());
|
||||
binding.signWith(deployment.getSigningKeyPair())
|
||||
binding.signWith(null, deployment.getSigningKeyPair())
|
||||
.signDocument();
|
||||
// TODO: As part of KEYCLOAK-3810, add KeyID to the SAML document
|
||||
// <related DocumentBuilder>.addExtension(new KeycloakKeySamlExtensionGenerator(<key ID>));
|
||||
}
|
||||
|
||||
binding.relayState("logout");
|
||||
|
||||
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.saml.rotation;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyName;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.cloned.HttpAdapterUtils;
|
||||
import org.keycloak.adapters.cloned.HttpClientAdapterException;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.processing.api.util.KeyInfoTools;
|
||||
|
||||
/**
|
||||
* This class defines a {@link KeyLocator} that looks up public keys and certificates in IdP's
|
||||
* SAML descriptor (i.e. http://{host}/auth/realms/{realm}/protocol/saml/descriptor).
|
||||
*
|
||||
* Based on {@code JWKPublicKeyLocator}.
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class SamlDescriptorPublicKeyLocator implements KeyLocator, Iterable<PublicKey> {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(SamlDescriptorPublicKeyLocator.class);
|
||||
|
||||
/**
|
||||
* Time between two subsequent requests (in seconds).
|
||||
*/
|
||||
private final int minTimeBetweenDescriptorRequests;
|
||||
|
||||
/**
|
||||
* Time to live for cache entries (in seconds).
|
||||
*/
|
||||
private final int cacheEntryTtl;
|
||||
|
||||
/**
|
||||
* Target descriptor URL.
|
||||
*/
|
||||
private final String descriptorUrl;
|
||||
|
||||
private final Map<String, PublicKey> publicKeyCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final HttpClient client;
|
||||
|
||||
private volatile int lastRequestTime = 0;
|
||||
|
||||
public SamlDescriptorPublicKeyLocator(String descriptorUrl, int minTimeBetweenDescriptorRequests, int cacheEntryTtl, HttpClient httpClient) {
|
||||
this.minTimeBetweenDescriptorRequests = minTimeBetweenDescriptorRequests <= 0
|
||||
? 20
|
||||
: minTimeBetweenDescriptorRequests;
|
||||
|
||||
this.descriptorUrl = descriptorUrl;
|
||||
this.cacheEntryTtl = cacheEntryTtl;
|
||||
|
||||
this.client = httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getKey(String kid) throws KeyManagementException {
|
||||
if (kid == null) {
|
||||
LOG.debugf("Invalid key id: %s", kid);
|
||||
return null;
|
||||
}
|
||||
|
||||
LOG.tracef("Requested key id: %s", kid);
|
||||
|
||||
int currentTime = Time.currentTime();
|
||||
|
||||
PublicKey res;
|
||||
if (currentTime > this.lastRequestTime + this.cacheEntryTtl) {
|
||||
LOG.debugf("Performing regular cache cleanup.");
|
||||
res = refreshCertificateCacheAndGet(kid);
|
||||
} else {
|
||||
res = publicKeyCache.get(kid);
|
||||
|
||||
if (res == null) {
|
||||
if (currentTime > this.lastRequestTime + this.minTimeBetweenDescriptorRequests) {
|
||||
res = refreshCertificateCacheAndGet(kid);
|
||||
} else {
|
||||
LOG.debugf("Won't send request to realm SAML descriptor url, timeout not expired. Last request time was %d", lastRequestTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void refreshKeyCache() {
|
||||
LOG.info("Forcing key cache cleanup and refresh.");
|
||||
this.publicKeyCache.clear();
|
||||
refreshCertificateCacheAndGet(null);
|
||||
}
|
||||
|
||||
private synchronized PublicKey refreshCertificateCacheAndGet(String kid) {
|
||||
if (this.descriptorUrl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.lastRequestTime = Time.currentTime();
|
||||
|
||||
LOG.debugf("Refreshing public key cache from %s", this.descriptorUrl);
|
||||
List<KeyInfo> signingCerts;
|
||||
try {
|
||||
MultivaluedHashMap<String, KeyInfo> certs = HttpAdapterUtils.downloadKeysFromSamlDescriptor(client, this.descriptorUrl);
|
||||
signingCerts = certs.get(KeyTypes.SIGNING.value());
|
||||
} catch (HttpClientAdapterException ex) {
|
||||
LOG.error("Could not refresh certificates from the server", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (signingCerts == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LOG.debugf("Certificates retrieved from server, filling public key cache");
|
||||
|
||||
// Only clear cache after it is certain that the SAML descriptor has been read successfully
|
||||
this.publicKeyCache.clear();
|
||||
|
||||
for (KeyInfo ki : signingCerts) {
|
||||
KeyName keyName = KeyInfoTools.getKeyName(ki);
|
||||
X509Certificate x509certificate = KeyInfoTools.getX509Certificate(ki);
|
||||
if (x509certificate != null && keyName != null) {
|
||||
LOG.tracef("Registering signing certificate %s", keyName.getName());
|
||||
this.publicKeyCache.put(keyName.getName(), x509certificate.getPublicKey());
|
||||
} else {
|
||||
LOG.tracef("Ignoring certificate %s: %s", keyName, x509certificate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (kid == null ? null : this.publicKeyCache.get(kid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Keys retrieved from SAML descriptor at " + descriptorUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<PublicKey> iterator() {
|
||||
if (this.publicKeyCache.isEmpty()) {
|
||||
refreshCertificateCacheAndGet(null);
|
||||
}
|
||||
|
||||
return this.publicKeyCache.values().iterator();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,451 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<xs:schema version="1.0"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="urn:keycloak:saml:adapter"
|
||||
targetNamespace="urn:keycloak:saml:adapter"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xs:element name="keycloak-saml-adapter" type="adapter-type"/>
|
||||
<xs:complexType name="adapter-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Keycloak SAML Adapter configuration file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:all>
|
||||
<xs:element name="SP" maxOccurs="1" minOccurs="0" type="sp-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes SAML service provider configuration.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="sp-type">
|
||||
<xs:all>
|
||||
<xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
List of service provider encryption and validation keys.
|
||||
|
||||
If the IDP requires that the client application (SP) sign all of its requests and/or if the IDP will encrypt assertions, you must define the keys used to do this. For client signed documents you must define both the private and public key or certificate that will be used to sign documents. For encryption, you only have to define the private key that will be used to decrypt.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="PrincipalNameMapping" type="principal-name-mapping-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>When creating a Java Principal object that you obtain from methods like HttpServletRequest.getUserPrincipal(), you can define what name that is returned by the Principal.getName() method.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="RoleIdentifiers" type="role-identifiers-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines what SAML attributes within the assertion received from the user should be used as role identifiers within the Java EE Security Context for the user.
|
||||
By default Role attribute values are converted to Java EE roles. Some IDPs send roles via a member or memberOf attribute assertion. You can define one or more Attribute elements to specify which SAML attributes must be converted into roles.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="IDP" type="idp-type" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes configuration of SAML identity provider for this service provider.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="entityID" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the identifier for this client. The IDP needs this value to determine who the client is that is communicating with it.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="sslPolicy" type="ssl-policy-type" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SSL policy the adapter will enforce.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="nameIDPolicyFormat" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SAML clients can request a specific NameID Subject format. Fill in this value if you want a specific format. It must be a standard SAML format identifier, i.e. urn:oasis:names:tc:SAML:2.0:nameid-format:transient. By default, no special format is requested.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="logoutPage" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL of the logout page.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="forceAuthentication" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SAML clients can request that a user is re-authenticated even if they are already logged in at the IDP. Default value is false.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="isPassive" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SAML clients can request that a user is never asked to authenticate even if they are not logged in at the IDP. Set this to true if you want this. Do not use together with forceAuthentication as they are opposite. Default value is false.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="turnOffChangeSessionIdOnLogin" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The session id is changed by default on a successful login on some platforms to plug a security attack vector. Change this to true to disable this. It is recommended you do not turn it off. Default value is false.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="keys-type">
|
||||
<xs:sequence>
|
||||
<xs:element name="Key" type="key-type" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a single key used for signing or encryption.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="key-type">
|
||||
<xs:all>
|
||||
<xs:element name="KeyStore" maxOccurs="1" minOccurs="0" type="key-store-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Java keystore to load keys and certificates from.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="PrivateKeyPem" type="xs:string" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Private key (PEM format)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="PublicKeyPem" type="xs:string" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Public key (PEM format)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="CertificatePem" type="xs:string" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Certificate key (PEM format)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="signing" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Flag defining whether the key should be used for signing.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="encryption" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Flag defining whether the key should be used for encryption</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="key-store-type">
|
||||
<xs:all>
|
||||
<xs:element name="PrivateKey" maxOccurs="1" minOccurs="0" type="private-key-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Private key declaration</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="Certificate" type="certificate-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Certificate declaration</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="file" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>File path to the key store.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="resource" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>WAR resource path to the key store. This is a path used in method call to ServletContext.getResourceAsStream().</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="password" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The password of the key store.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="private-key-type">
|
||||
<xs:attribute name="alias" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Alias that points to the key or cert within the keystore.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="password" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Keystores require an additional password to access private keys. In the PrivateKey element you must define this password within a password attribute.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="certificate-type">
|
||||
<xs:attribute name="alias" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Alias that points to the key or cert within the keystore.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="principal-name-mapping-type">
|
||||
<xs:attribute name="policy" type="principal-name-mapping-policy-type" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Policy used to populate value of Java Principal object obtained from methods like HttpServletRequest.getUserPrincipal().</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="attribute" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Name of the SAML assertion attribute to use within.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="principal-name-mapping-policy-type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="FROM_NAME_ID">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This policy just uses whatever the SAML subject value is. This is the default setting</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="FROM_ATTRIBUTE">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This will pull the value from one of the attributes declared in the SAML assertion received from the server. You'll need to specify the name of the SAML assertion attribute to use within the attribute XML attribute.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="ssl-policy-type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="ALL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>All requests must come in via HTTPS.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="EXTERNAL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Only non-private IP addresses must come over the wire via HTTPS.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="NONE">
|
||||
<xs:annotation>
|
||||
<xs:documentation>no requests are required to come over via HTTPS.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="signature-algorithm-type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="RSA_SHA1"/>
|
||||
<xs:enumeration value="RSA_SHA256"/>
|
||||
<xs:enumeration value="RSA_SHA512"/>
|
||||
<xs:enumeration value="DSA_SHA1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="binding-type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="POST"/>
|
||||
<xs:enumeration value="REDIRECT"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="role-identifiers-type">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="Attribute" maxOccurs="unbounded" minOccurs="0" type="attribute-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies SAML attribute to be converted into roles.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="attribute-type">
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies name of the SAML attribute to be converted into roles.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="idp-type">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="SingleSignOnService" maxOccurs="1" minOccurs="1" type="sign-on-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Configuration of the login SAML endpoint of the IDP.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="SingleLogoutService" type="logout-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Configuration of the logout SAML endpoint of the IDP</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The Keys sub element of IDP is only used to define the certificate or public key to use to verify documents signed by the IDP.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="HttpClient" type="http-client-type" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Configuration of HTTP client used for automatic obtaining of certificates containing public keys for IDP signature verification via SAML descriptor of the IDP.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="entityID" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>issuer ID of the IDP.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="signaturesRequired" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If set to true, the client adapter will sign every document it sends to the IDP. Also, the client will expect that the IDP will be signing any documents sent to it. This switch sets the default for all request and response types.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="signatureAlgorithm" type="signature-algorithm-type" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Signature algorithm that the IDP expects signed documents to use. Defaults to RSA_SHA256</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="signatureCanonicalizationMethod" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the signature canonicalization method that the IDP expects signed documents to use. The default value is http://www.w3.org/2001/10/xml-exc-c14n# and should be good for most IDPs.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="encryption" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="sign-on-type">
|
||||
<xs:attribute name="signRequest" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client sign authn requests? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client expect the IDP to sign the assertion response document sent back from an auhtn request? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="validateAssertionSignature" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client expect the IDP to sign the individual assertions sent back from an auhtn request? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="requestBinding" type="binding-type" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SAML binding type used for communicating with the IDP. The default value is POST, but you can set it to REDIRECT as well.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="responseBinding" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SAML allows the client to request what binding type it wants authn responses to use. The default is that the client will not request a specific binding type for responses.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="bindingUrl" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the URL for the IDP login service that the client will send requests to.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="logout-type">
|
||||
<xs:attribute name="signRequest" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client sign authn requests? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="signResponse" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client sign logout responses it sends to the IDP requests? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="validateRequestSignature" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client expect signed logout request documents from the IDP? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Should the client expect signed logout response documents from the IDP? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="requestBinding" type="binding-type" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the SAML binding type used for communicating SAML requests to the IDP. The default value is POST.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="responseBinding" type="binding-type" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the SAML binding type used for communicating SAML responses to the IDP. The default value is POST.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="postBindingUrl" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the URL for the IDP's logout service when using the POST binding. This setting is REQUIRED if using the POST binding.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="redirectBindingUrl" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the URL for the IDP's logout service when using the REDIRECT binding. This setting is REQUIRED if using the REDIRECT binding.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="http-client-type">
|
||||
<xs:attribute name="allowAnyHostname" type="xs:boolean" use="optional" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If the the IDP server requires HTTPS and this config option is set to true the IDP's certificate
|
||||
is validated via the truststore, but host name validation is not done. This setting should only be used during
|
||||
development and never in production as it will partly disable verification of SSL certificates.
|
||||
This seting may be useful in test environments. The default value is false.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="clientKeystore" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is the file path to a keystore file. This keystore contains client certificate
|
||||
for two-way SSL when the adapter makes HTTPS requests to the IDP server.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="clientKeystorePassword" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Password for the client keystore and for the client's key.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="connectionPoolSize" type="xs:int" use="optional" default="10">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines number of pooled connections.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="disableTrustManager" type="xs:boolean" use="optional" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If the the IDP server requires HTTPS and this config option is set to true you do not have to specify a truststore.
|
||||
This setting should only be used during development and never in production as it will disable verification of SSL certificates.
|
||||
The default value is false.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="proxyUrl" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL to HTTP proxy to use for HTTP connections.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="truststore" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The value is the file path to a keystore file. If you prefix the path with classpath:,
|
||||
then the truststore will be obtained from the deployment's classpath instead. Used for outgoing
|
||||
HTTPS communications to the IDP server. Client making 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.
|
||||
You can create this truststore by extracting the public certificate of the IDP's SSL keystore.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="truststorePassword" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Password for the truststore keystore.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
||||
@ -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,180 @@
|
||||
/*
|
||||
* 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.saml.config.parsers;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.saml.config.IDP;
|
||||
import org.keycloak.adapters.saml.config.Key;
|
||||
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
|
||||
import org.keycloak.adapters.saml.config.SP;
|
||||
import org.keycloak.saml.common.util.StaxParserUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakSamlAdapterXMLParserTest {
|
||||
|
||||
private static final String CURRENT_XSD_LOCATION = "/schema/keycloak_saml_adapter_1_7.xsd";
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private void testValidationValid(String fileName) throws Exception {
|
||||
InputStream schema = getClass().getResourceAsStream(CURRENT_XSD_LOCATION);
|
||||
InputStream is = getClass().getResourceAsStream(fileName);
|
||||
assertNotNull(is);
|
||||
assertNotNull(schema);
|
||||
StaxParserUtil.validate(is, schema);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationSimpleFile() throws Exception {
|
||||
testValidationValid("keycloak-saml.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationMultipleKeys() throws Exception {
|
||||
testValidationValid("keycloak-saml-multiple-signing-keys.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationWithHttpClient() throws Exception {
|
||||
testValidationValid("keycloak-saml-wth-http-client-settings.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationKeyInvalid() throws Exception {
|
||||
InputStream schemaIs = KeycloakSamlAdapterXMLParser.class.getResourceAsStream(CURRENT_XSD_LOCATION);
|
||||
InputStream is = getClass().getResourceAsStream("keycloak-saml-invalid.xml");
|
||||
assertNotNull(is);
|
||||
assertNotNull(schemaIs);
|
||||
|
||||
expectedException.expect(ParsingException.class);
|
||||
StaxParserUtil.validate(is, schemaIs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXmlParser() throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream("keycloak-saml.xml");
|
||||
assertNotNull(is);
|
||||
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
|
||||
|
||||
KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
|
||||
assertNotNull(config);
|
||||
assertEquals(1, config.getSps().size());
|
||||
SP sp = config.getSps().get(0);
|
||||
assertEquals("sp", sp.getEntityID());
|
||||
assertEquals("EXTERNAL", sp.getSslPolicy());
|
||||
assertEquals("format", sp.getNameIDPolicyFormat());
|
||||
assertTrue(sp.isForceAuthentication());
|
||||
assertTrue(sp.isIsPassive());
|
||||
assertEquals(2, sp.getKeys().size());
|
||||
Key signing = sp.getKeys().get(0);
|
||||
assertTrue(signing.isSigning());
|
||||
Key.KeyStoreConfig keystore = signing.getKeystore();
|
||||
assertNotNull(keystore);
|
||||
assertEquals("file", keystore.getFile());
|
||||
assertEquals("cp", keystore.getResource());
|
||||
assertEquals("pw", keystore.getPassword());
|
||||
assertEquals("private alias", keystore.getPrivateKeyAlias());
|
||||
assertEquals("private pw", keystore.getPrivateKeyPassword());
|
||||
assertEquals("cert alias", keystore.getCertificateAlias());
|
||||
Key encryption = sp.getKeys().get(1);
|
||||
assertTrue(encryption.isEncryption());
|
||||
assertEquals("private pem", encryption.getPrivateKeyPem());
|
||||
assertEquals("public pem", encryption.getPublicKeyPem());
|
||||
assertEquals("FROM_ATTRIBUTE", sp.getPrincipalNameMapping().getPolicy());
|
||||
assertEquals("attribute", sp.getPrincipalNameMapping().getAttributeName());
|
||||
assertTrue(sp.getRoleAttributes().size() == 1);
|
||||
assertTrue(sp.getRoleAttributes().contains("member"));
|
||||
|
||||
IDP idp = sp.getIdp();
|
||||
assertEquals("idp", idp.getEntityID());
|
||||
assertEquals("RSA_SHA256", idp.getSignatureAlgorithm());
|
||||
assertEquals("canon", idp.getSignatureCanonicalizationMethod());
|
||||
assertTrue(idp.getSingleSignOnService().isSignRequest());
|
||||
assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
|
||||
assertEquals("POST", idp.getSingleSignOnService().getRequestBinding());
|
||||
assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
|
||||
|
||||
assertTrue(idp.getSingleLogoutService().isSignRequest());
|
||||
assertTrue(idp.getSingleLogoutService().isSignResponse());
|
||||
assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
|
||||
assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
|
||||
assertEquals("REDIRECT", idp.getSingleLogoutService().getRequestBinding());
|
||||
assertEquals("POST", idp.getSingleLogoutService().getResponseBinding());
|
||||
assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
|
||||
assertEquals("redirecturl", idp.getSingleLogoutService().getRedirectBindingUrl());
|
||||
|
||||
assertTrue(idp.getKeys().size() == 1);
|
||||
assertTrue(idp.getKeys().get(0).isSigning());
|
||||
assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testXmlParserMultipleSigningKeys() throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream("keycloak-saml-multiple-signing-keys.xml");
|
||||
assertNotNull(is);
|
||||
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
|
||||
|
||||
KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
|
||||
assertNotNull(config);
|
||||
assertEquals(1, config.getSps().size());
|
||||
SP sp = config.getSps().get(0);
|
||||
IDP idp = sp.getIdp();
|
||||
|
||||
assertTrue(idp.getKeys().size() == 4);
|
||||
for (int i = 0; i < 4; i ++) {
|
||||
Key key = idp.getKeys().get(i);
|
||||
assertTrue(key.isSigning());
|
||||
assertEquals("cert pem " + i, idp.getKeys().get(i).getCertificatePem());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXmlParserHttpClientSettings() throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream("keycloak-saml-wth-http-client-settings.xml");
|
||||
assertNotNull(is);
|
||||
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
|
||||
|
||||
KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
|
||||
assertNotNull(config);
|
||||
assertEquals(1, config.getSps().size());
|
||||
SP sp = config.getSps().get(0);
|
||||
IDP idp = sp.getIdp();
|
||||
|
||||
assertThat(idp.getHttpClientConfig(), notNullValue());
|
||||
assertThat(idp.getHttpClientConfig().getClientKeystore(), is("ks"));
|
||||
assertThat(idp.getHttpClientConfig().getClientKeystorePassword(), is("ks-pwd"));
|
||||
assertThat(idp.getHttpClientConfig().getProxyUrl(), is("pu"));
|
||||
assertThat(idp.getHttpClientConfig().getTruststore(), is("ts"));
|
||||
assertThat(idp.getHttpClientConfig().getTruststorePassword(), is("tsp"));
|
||||
assertThat(idp.getHttpClientConfig().getConnectionPoolSize(), is(42));
|
||||
assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true));
|
||||
assertThat(idp.getHttpClientConfig().isDisableTrustManager(), is(true));
|
||||
}
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
/*
|
||||
* 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.test.adapters.saml;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.saml.config.IDP;
|
||||
import org.keycloak.adapters.saml.config.Key;
|
||||
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
|
||||
import org.keycloak.adapters.saml.config.SP;
|
||||
import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterXMLParser;
|
||||
import org.keycloak.saml.common.util.StaxParserUtil;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class XmlParserTest {
|
||||
|
||||
@Test
|
||||
public void testValidation() throws Exception {
|
||||
{
|
||||
InputStream schema = KeycloakSamlAdapterXMLParser.class.getResourceAsStream("/schema/keycloak_saml_adapter_1_6.xsd");
|
||||
InputStream is = getClass().getResourceAsStream("/keycloak-saml.xml");
|
||||
Assert.assertNotNull(is);
|
||||
Assert.assertNotNull(schema);
|
||||
StaxParserUtil.validate(is, schema);
|
||||
}
|
||||
{
|
||||
InputStream sch = KeycloakSamlAdapterXMLParser.class.getResourceAsStream("/schema/keycloak_saml_adapter_1_6.xsd");
|
||||
InputStream doc = getClass().getResourceAsStream("/keycloak-saml2.xml");
|
||||
Assert.assertNotNull(doc);
|
||||
Assert.assertNotNull(sch);
|
||||
try {
|
||||
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
Schema schema = factory.newSchema(new StreamSource(sch));
|
||||
Validator validator = schema.newValidator();
|
||||
StreamSource source = new StreamSource(doc);
|
||||
source.setSystemId("/keycloak-saml2.xml");
|
||||
validator.validate(source);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXmlParser() throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream("/keycloak-saml.xml");
|
||||
Assert.assertNotNull(is);
|
||||
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
|
||||
KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
|
||||
Assert.assertNotNull(config);
|
||||
Assert.assertEquals(1, config.getSps().size());
|
||||
SP sp = config.getSps().get(0);
|
||||
Assert.assertEquals("sp", sp.getEntityID());
|
||||
Assert.assertEquals("ssl", sp.getSslPolicy());
|
||||
Assert.assertEquals("format", sp.getNameIDPolicyFormat());
|
||||
Assert.assertTrue(sp.isForceAuthentication());
|
||||
Assert.assertTrue(sp.isIsPassive());
|
||||
Assert.assertEquals(2, sp.getKeys().size());
|
||||
Key signing = sp.getKeys().get(0);
|
||||
Assert.assertTrue(signing.isSigning());
|
||||
Key.KeyStoreConfig keystore = signing.getKeystore();
|
||||
Assert.assertNotNull(keystore);
|
||||
Assert.assertEquals("file", keystore.getFile());
|
||||
Assert.assertEquals("cp", keystore.getResource());
|
||||
Assert.assertEquals("pw", keystore.getPassword());
|
||||
Assert.assertEquals("private alias", keystore.getPrivateKeyAlias());
|
||||
Assert.assertEquals("private pw", keystore.getPrivateKeyPassword());
|
||||
Assert.assertEquals("cert alias", keystore.getCertificateAlias());
|
||||
Key encryption = sp.getKeys().get(1);
|
||||
Assert.assertTrue(encryption.isEncryption());
|
||||
Assert.assertEquals("private pem", encryption.getPrivateKeyPem());
|
||||
Assert.assertEquals("public pem", encryption.getPublicKeyPem());
|
||||
Assert.assertEquals("policy", sp.getPrincipalNameMapping().getPolicy());
|
||||
Assert.assertEquals("attribute", sp.getPrincipalNameMapping().getAttributeName());
|
||||
Assert.assertTrue(sp.getRoleAttributes().size() == 1);
|
||||
Assert.assertTrue(sp.getRoleAttributes().contains("member"));
|
||||
|
||||
IDP idp = sp.getIdp();
|
||||
Assert.assertEquals("idp", idp.getEntityID());
|
||||
Assert.assertEquals("RSA", idp.getSignatureAlgorithm());
|
||||
Assert.assertEquals("canon", idp.getSignatureCanonicalizationMethod());
|
||||
Assert.assertTrue(idp.getSingleSignOnService().isSignRequest());
|
||||
Assert.assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
|
||||
Assert.assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
|
||||
Assert.assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
|
||||
|
||||
Assert.assertTrue(idp.getSingleLogoutService().isSignRequest());
|
||||
Assert.assertTrue(idp.getSingleLogoutService().isSignResponse());
|
||||
Assert.assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
|
||||
Assert.assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
|
||||
Assert.assertEquals("redirect", idp.getSingleLogoutService().getRequestBinding());
|
||||
Assert.assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
|
||||
Assert.assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
|
||||
Assert.assertEquals("redirecturl", idp.getSingleLogoutService().getRedirectBindingUrl());
|
||||
|
||||
Assert.assertTrue(idp.getKeys().size() == 1);
|
||||
Assert.assertTrue(idp.getKeys().get(0).isSigning());
|
||||
Assert.assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -0,0 +1,83 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="sp"
|
||||
sslPolicy="ALL"
|
||||
nameIDPolicyFormat="format"
|
||||
forceAuthentication="true"
|
||||
isPassive="true">
|
||||
<Keys>
|
||||
<Key signing="true" >
|
||||
<KeyStore file="file" resource="cp" password="pw">
|
||||
<PrivateKey alias="private alias" password="private pw"/>
|
||||
<Certificate alias="cert alias"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
<Key encryption="true">
|
||||
<PrivateKeyPem>
|
||||
private pem
|
||||
</PrivateKeyPem>
|
||||
<PublicKeyPem>
|
||||
public pem
|
||||
</PublicKeyPem>
|
||||
</Key>
|
||||
</Keys>
|
||||
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
|
||||
<RoleIdentifiers>
|
||||
<Attribute name="member"/>
|
||||
</RoleIdentifiers>
|
||||
<IDP entityID="idp"
|
||||
signatureAlgorithm="RSA_SHA512"
|
||||
signatureCanonicalizationMethod="canon"
|
||||
signaturesRequired="true"
|
||||
>
|
||||
<SingleSignOnService signRequest="true"
|
||||
validateResponseSignature="true"
|
||||
requestBinding="POST"
|
||||
bindingUrl="bindingurl"
|
||||
/>
|
||||
|
||||
<SingleLogoutService
|
||||
validateRequestSignature="true"
|
||||
validateResponseSignature="true"
|
||||
signRequest="true"
|
||||
signResponse="true"
|
||||
requestBinding="REDIRECT"
|
||||
responseBinding="POST"
|
||||
postBindingUrl="posturl"
|
||||
redirectBindingUrl="redirecturl"
|
||||
/>
|
||||
<Keys>
|
||||
<Key signing="true">
|
||||
<CertificatePem>cert pem 0</CertificatePem>
|
||||
</Key>
|
||||
<Key signing="true">
|
||||
<CertificatePem>cert pem 1</CertificatePem>
|
||||
</Key>
|
||||
<Key signing="true">
|
||||
<CertificatePem>cert pem 2</CertificatePem>
|
||||
</Key>
|
||||
<Key signing="true">
|
||||
<CertificatePem>cert pem 3</CertificatePem>
|
||||
</Key>
|
||||
</Keys>
|
||||
</IDP>
|
||||
</SP>
|
||||
</keycloak-saml-adapter>
|
||||
@ -0,0 +1,83 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="sp"
|
||||
sslPolicy="ALL"
|
||||
nameIDPolicyFormat="format"
|
||||
forceAuthentication="true"
|
||||
isPassive="true">
|
||||
<Keys>
|
||||
<Key signing="true" >
|
||||
<KeyStore file="file" resource="cp" password="pw">
|
||||
<PrivateKey alias="private alias" password="private pw"/>
|
||||
<Certificate alias="cert alias"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
<Key encryption="true">
|
||||
<PrivateKeyPem>
|
||||
private pem
|
||||
</PrivateKeyPem>
|
||||
<PublicKeyPem>
|
||||
public pem
|
||||
</PublicKeyPem>
|
||||
</Key>
|
||||
</Keys>
|
||||
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
|
||||
<RoleIdentifiers>
|
||||
<Attribute name="member"/>
|
||||
</RoleIdentifiers>
|
||||
<IDP entityID="idp"
|
||||
signatureAlgorithm="RSA_SHA512"
|
||||
signatureCanonicalizationMethod="canon"
|
||||
signaturesRequired="true"
|
||||
>
|
||||
<SingleSignOnService signRequest="true"
|
||||
validateResponseSignature="true"
|
||||
requestBinding="POST"
|
||||
bindingUrl="url"
|
||||
/>
|
||||
|
||||
<SingleLogoutService
|
||||
validateRequestSignature="true"
|
||||
validateResponseSignature="true"
|
||||
signRequest="true"
|
||||
signResponse="true"
|
||||
requestBinding="REDIRECT"
|
||||
responseBinding="POST"
|
||||
postBindingUrl="posturl"
|
||||
redirectBindingUrl="redirecturl"
|
||||
/>
|
||||
<Keys>
|
||||
<Key signing="true">
|
||||
<CertificatePem>
|
||||
cert pem
|
||||
</CertificatePem>
|
||||
</Key>
|
||||
</Keys>
|
||||
<HttpClient allowAnyHostname="true"
|
||||
clientKeystore="ks" clientKeystorePassword="ks-pwd"
|
||||
connectionPoolSize="42"
|
||||
disableTrustManager="true"
|
||||
proxyUrl="pu"
|
||||
truststore="ts" truststorePassword="tsp"
|
||||
/>
|
||||
</IDP>
|
||||
</SP>
|
||||
</keycloak-saml-adapter>
|
||||
@ -15,14 +15,16 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter">
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="sp"
|
||||
sslPolicy="ssl"
|
||||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="format"
|
||||
forceAuthentication="true"
|
||||
isPassive="true">
|
||||
<Keys>
|
||||
<Key signing="true" >
|
||||
<Key signing="true">
|
||||
<KeyStore file="file" resource="cp" password="pw">
|
||||
<PrivateKey alias="private alias" password="private pw"/>
|
||||
<Certificate alias="cert alias"/>
|
||||
@ -37,18 +39,18 @@
|
||||
</PublicKeyPem>
|
||||
</Key>
|
||||
</Keys>
|
||||
<PrincipalNameMapping policy="policy" attribute="attribute"/>
|
||||
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
|
||||
<RoleIdentifiers>
|
||||
<Attribute name="member"/>
|
||||
</RoleIdentifiers>
|
||||
<IDP entityID="idp"
|
||||
signatureAlgorithm="RSA"
|
||||
signatureAlgorithm="RSA_SHA256"
|
||||
signatureCanonicalizationMethod="canon"
|
||||
signaturesRequired="true"
|
||||
>
|
||||
<SingleSignOnService signRequest="true"
|
||||
validateResponseSignature="true"
|
||||
requestBinding="post"
|
||||
requestBinding="POST"
|
||||
bindingUrl="url"
|
||||
/>
|
||||
|
||||
@ -57,8 +59,8 @@
|
||||
validateResponseSignature="true"
|
||||
signRequest="true"
|
||||
signResponse="true"
|
||||
requestBinding="redirect"
|
||||
responseBinding="post"
|
||||
requestBinding="REDIRECT"
|
||||
responseBinding="POST"
|
||||
postBindingUrl="posturl"
|
||||
redirectBindingUrl="redirecturl"
|
||||
/>
|
||||
@ -46,6 +46,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -23,6 +23,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
|
||||
@ -98,7 +98,7 @@ public final class StringPropertyReplacer
|
||||
public static String replaceProperties(final String string, final Properties props)
|
||||
{
|
||||
final char[] chars = string.toCharArray();
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
boolean properties = false;
|
||||
int state = NORMAL;
|
||||
int start = 0;
|
||||
|
||||
@ -26,26 +26,51 @@ public class Time {
|
||||
|
||||
private static int offset;
|
||||
|
||||
/**
|
||||
* Returns current time in seconds adjusted by adding {@link #offset) seconds.
|
||||
* @return see description
|
||||
*/
|
||||
public static int currentTime() {
|
||||
return ((int) (System.currentTimeMillis() / 1000)) + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current time in milliseconds adjusted by adding {@link #offset) seconds.
|
||||
* @return see description
|
||||
*/
|
||||
public static long currentTimeMillis() {
|
||||
return System.currentTimeMillis() + (offset * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Date} object, its value set to time
|
||||
* @param time Time in milliseconds since the epoch
|
||||
* @return see description
|
||||
*/
|
||||
public static Date toDate(int time) {
|
||||
return new Date(((long) time ) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time in milliseconds for a time in seconds. No adjustment is made to the parameter.
|
||||
* @param time Time in seconds since the epoch
|
||||
* @return Time in milliseconds
|
||||
*/
|
||||
public static long toMillis(int time) {
|
||||
return ((long) time) * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Time offset in seconds that will be added to {@link #currentTime()} and {@link #currentTimeMillis()}.
|
||||
*/
|
||||
public static int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time offset in seconds that will be added to {@link #currentTime()} and {@link #currentTimeMillis()}.
|
||||
* @param offset Offset (in seconds)
|
||||
*/
|
||||
public static void setOffset(int offset) {
|
||||
Time.offset = offset;
|
||||
}
|
||||
|
||||
@ -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",
|
||||
"policy-enforcer"
|
||||
})
|
||||
public class AdapterConfig extends BaseAdapterConfig {
|
||||
public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClientConfig {
|
||||
|
||||
@JsonProperty("allow-any-hostname")
|
||||
protected boolean allowAnyHostname;
|
||||
@ -82,6 +82,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
@JsonProperty("proxy-url")
|
||||
protected String proxyUrl;
|
||||
|
||||
@Override
|
||||
public boolean isAllowAnyHostname() {
|
||||
return allowAnyHostname;
|
||||
}
|
||||
@ -90,6 +91,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.allowAnyHostname = allowAnyHostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisableTrustManager() {
|
||||
return disableTrustManager;
|
||||
}
|
||||
@ -98,6 +100,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.disableTrustManager = disableTrustManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTruststore() {
|
||||
return truststore;
|
||||
}
|
||||
@ -106,6 +109,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.truststore = truststore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTruststorePassword() {
|
||||
return truststorePassword;
|
||||
}
|
||||
@ -114,6 +118,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.truststorePassword = truststorePassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientKeystore() {
|
||||
return clientKeystore;
|
||||
}
|
||||
@ -122,6 +127,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.clientKeystore = clientKeystore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientKeystorePassword() {
|
||||
return clientKeystorePassword;
|
||||
}
|
||||
@ -138,6 +144,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.clientKeyPassword = clientKeyPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConnectionPoolSize() {
|
||||
return connectionPoolSize;
|
||||
}
|
||||
@ -202,6 +209,7 @@ public class AdapterConfig extends BaseAdapterConfig {
|
||||
this.policyEnforcerConfig = policyEnforcerConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProxyUrl() {
|
||||
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();
|
||||
|
||||
}
|
||||
4
dependencies/server-min/pom.xml
vendored
4
dependencies/server-min/pom.xml
vendored
@ -59,6 +59,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="com.thoughtworks.xstream"/>
|
||||
<module name="org.antlr" slot="3.5"/>
|
||||
<module name="org.kie"/>
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.keycloak.keycloak-services"/>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.keycloak.keycloak-services"/>
|
||||
<module name="org.kie"/>
|
||||
</dependencies>
|
||||
|
||||
@ -16,10 +16,6 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-common">
|
||||
<properties>
|
||||
<property name="jboss.api" value="private"/>
|
||||
</properties>
|
||||
|
||||
<resources>
|
||||
<artifact name="${org.keycloak:keycloak-common}"/>
|
||||
</resources>
|
||||
|
||||
@ -16,10 +16,6 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-core">
|
||||
<properties>
|
||||
<property name="jboss.api" value="private"/>
|
||||
</properties>
|
||||
|
||||
<resources>
|
||||
<artifact name="${org.keycloak:keycloak-core}"/>
|
||||
</resources>
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.keycloak.keycloak-kerberos-federation"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.infinispan"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="javax.api"/>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="javax.persistence.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.liquibase"/>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-services"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.mongodb.mongo-java-driver"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="javax.api"/>
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
<?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.
|
||||
-->
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-server-spi-private">
|
||||
<properties>
|
||||
<property name="jboss.api" value="private"/>
|
||||
</properties>
|
||||
|
||||
<resources>
|
||||
<artifact name="${org.keycloak:keycloak-server-spi-private}"/>
|
||||
</resources>
|
||||
|
||||
<dependencies>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
<module name="javax.transaction.api"/>
|
||||
</dependencies>
|
||||
</module>
|
||||
@ -16,10 +16,6 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-server-spi">
|
||||
<properties>
|
||||
<property name="jboss.api" value="private"/>
|
||||
</properties>
|
||||
|
||||
<resources>
|
||||
<artifact name="${org.keycloak:keycloak-server-spi}"/>
|
||||
</resources>
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
<module name="org.keycloak.keycloak-ldap-federation" services="import"/>
|
||||
<module name="org.keycloak.keycloak-sssd-federation" services="import"/>
|
||||
<module name="org.keycloak.keycloak-server-spi" services="import"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private" services="import"/>
|
||||
<module name="org.keycloak.keycloak-model-jpa" services="import"/>
|
||||
<module name="org.keycloak.keycloak-model-mongo" services="import"/>
|
||||
<module name="org.keycloak.keycloak-model-infinispan" services="import"/>
|
||||
|
||||
@ -28,5 +28,6 @@
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.keycloak.keycloak-core" />
|
||||
<module name="org.keycloak.keycloak-server-spi" />
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
</dependencies>
|
||||
</module>
|
||||
@ -30,6 +30,7 @@
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.jboss.aesh"/>
|
||||
<module name="org.jboss.as.domain-management"/>
|
||||
<module name="com.fasterxml.jackson.core.jackson-core"/>
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.keycloak.keycloak-services"/>
|
||||
<module name="org.jboss.modules"/>
|
||||
</dependencies>
|
||||
|
||||
@ -32,11 +32,13 @@
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.slf4j"/>
|
||||
<module name="org.apache.commons.logging"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="com.sun.xml.bind"/>
|
||||
<module name="com.thoughtworks.xstream"/>
|
||||
<module name="org.apache.ant"/>
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-adapter-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -39,5 +39,6 @@
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.jboss.vfs"/>
|
||||
<module name="org.jboss.metadata"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
</module>
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
</imports>
|
||||
</module>
|
||||
<module name="javax.api"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
</imports>
|
||||
</module>
|
||||
<module name="javax.api"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
<module name="org.picketbox"/>
|
||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
</imports>
|
||||
</module>
|
||||
<module name="javax.api"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
</imports>
|
||||
</module>
|
||||
<module name="javax.api"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-adapter-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
||||
<module name="org.keycloak.keycloak-saml-adapter-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
||||
@ -39,5 +39,6 @@
|
||||
<module name="org.jboss.vfs"/>
|
||||
<module name="org.jboss.as.web-common"/>
|
||||
<module name="org.jboss.metadata"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
</module>
|
||||
|
||||
@ -41,6 +41,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@ -3,7 +3,7 @@ Example Domain Extension
|
||||
|
||||
To run, deploy as a module by running:
|
||||
|
||||
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist,org.liquibase"
|
||||
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist,org.liquibase"
|
||||
|
||||
|
||||
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
|
||||
|
||||
@ -46,6 +46,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-jpa</artifactId>
|
||||
|
||||
@ -3,7 +3,7 @@ Example Event Listener that prints events to System.out
|
||||
|
||||
To deploy copy target/event-listener-sysout-example.jar to providers directory. Alternatively you can deploy as a module by running:
|
||||
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
|
||||
|
||||
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
|
||||
|
||||
|
||||
@ -40,6 +40,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -3,7 +3,7 @@ Example Event Store that stores events in memory
|
||||
|
||||
To deploy copy target/event-store-mem-example.jar to providers directory. Alternatively you can deploy as a module by running:
|
||||
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
|
||||
|
||||
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
|
||||
|
||||
|
||||
@ -40,6 +40,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -4,7 +4,7 @@ Example User Federation Provider
|
||||
This is an example of user federation backed by a simple properties file. This properties file only contains username/password
|
||||
key pairs. To deploy, build this directory then take the jar and copy it to providers directory. Alternatively you can deploy as a module by running:
|
||||
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
|
||||
|
||||
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
|
||||
|
||||
|
||||
@ -41,6 +41,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@ -3,7 +3,7 @@ Example Realm REST Resource provider
|
||||
|
||||
To deploy copy target/hello-rest-example.jar to providers directory. Alternatively you can deploy as a module by running:
|
||||
|
||||
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,javax.ws.rs.api"
|
||||
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,javax.ws.rs.api"
|
||||
|
||||
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
|
||||
|
||||
|
||||
@ -41,6 +41,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.ws.rs</groupId>
|
||||
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
|
||||
|
||||
@ -30,7 +30,6 @@ import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.OnUserCache;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
@ -49,6 +48,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@ -139,7 +139,7 @@ public class EjbExampleUserStorageProvider implements UserStorageProvider,
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserEntity entity = new UserEntity();
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
entity.setId(UUID.randomUUID().toString());
|
||||
entity.setUsername(username);
|
||||
em.persist(entity);
|
||||
logger.info("added user: " + username);
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter>
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="http://localhost:8080/sales-post-enc/"
|
||||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter>
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="http://localhost:8080/sales-post-sig/"
|
||||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter>
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="http://localhost:8080/employee-sig/"
|
||||
sslPolicy="EXTERNAL"
|
||||
logoutPage="/logout.jsp"
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter>
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="http://localhost:8080/saml-servlet-filter/"
|
||||
sslPolicy="EXTERNAL"
|
||||
logoutPage="/logout.jsp">
|
||||
|
||||
@ -40,6 +40,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@ -33,7 +33,7 @@ import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.UserManager;
|
||||
import org.keycloak.models.UserManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
@ -40,6 +40,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-kerberos-federation</artifactId>
|
||||
|
||||
@ -47,7 +47,7 @@ import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.UserManager;
|
||||
import org.keycloak.models.UserManager;
|
||||
|
||||
import javax.naming.AuthenticationException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -38,6 +38,7 @@ import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
import org.keycloak.models.utils.UserModelDelegate;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -563,7 +564,7 @@ public class GroupLDAPFederationMapper extends AbstractLDAPFederationMapper impl
|
||||
|
||||
@Override
|
||||
public boolean hasRole(RoleModel role) {
|
||||
return super.hasRole(role) || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
return super.hasRole(role) || RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -38,6 +38,7 @@ import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
import org.keycloak.models.utils.UserModelDelegate;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -348,8 +349,8 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper imple
|
||||
@Override
|
||||
public boolean hasRole(RoleModel role) {
|
||||
Set<RoleModel> roles = getRoleMappings();
|
||||
return KeycloakModelUtils.hasRole(roles, role)
|
||||
|| KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
return RoleUtils.hasRole(roles, role)
|
||||
|| RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -60,6 +60,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@ -34,7 +34,7 @@ import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.services.managers.UserManager;
|
||||
import org.keycloak.models.UserManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
@ -38,6 +38,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@ -27,6 +27,7 @@ import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -306,7 +307,7 @@ public class UserAdapter implements CachedUserModel {
|
||||
for (RoleModel mapping: mappings) {
|
||||
if (mapping.hasRole(role)) return true;
|
||||
}
|
||||
return KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
return RoleUtils.hasRoleFromGroup(getGroups(), role, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -373,7 +374,7 @@ public class UserAdapter implements CachedUserModel {
|
||||
if (updated != null) return updated.isMemberOf(group);
|
||||
if (cached.getGroups().contains(group.getId())) return true;
|
||||
Set<GroupModel> roles = getGroups();
|
||||
return KeycloakModelUtils.isMember(roles, group);
|
||||
return RoleUtils.isMember(roles, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.keys.infinispan;
|
||||
package org.keycloak.keys.infinispan;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.Collections;
|
||||
@ -40,8 +40,6 @@ import org.junit.Test;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.keys.PublicKeyLoader;
|
||||
import org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProvider;
|
||||
import org.keycloak.keys.infinispan.PublicKeysEntry;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
@ -48,6 +48,10 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
|
||||
@ -27,6 +27,7 @@ import org.keycloak.models.jpa.entities.GroupAttributeEntity;
|
||||
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||
import org.keycloak.models.jpa.entities.GroupRoleMappingEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
@ -228,7 +229,7 @@ public class GroupAdapter implements GroupModel , JpaModel<GroupEntity> {
|
||||
@Override
|
||||
public boolean hasRole(RoleModel role) {
|
||||
Set<RoleModel> roles = getRoleMappings();
|
||||
return KeycloakModelUtils.hasRole(roles, role);
|
||||
return RoleUtils.hasRole(roles, role);
|
||||
}
|
||||
|
||||
protected TypedQuery<GroupRoleMappingEntity> getGroupRoleMappingEntityTypedQuery(RoleModel role) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user